2
\$\begingroup\$

The following codes generate table row dynamically. I wonder if there are better ways of generating the same data. Also instead of manually coding the heading and the table itself, is it possible to code them with less codes without using the innerHTML function.

<table>
    <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Mark Score</th>
        </tr>
    </thead>
</table>

Data:

const students = {
        '24': {
            'id':'24',
            'Name':'Zuali',
            'mark':30,
        },
        '25': {
            'id':'25',
            'Name':'Famkima',
            'mark':52,
        },
        '27':{
            'id':'27',
            'Name':'Duha',
            'mark':77,
        },
        '28':{
            'id':'28',
            'Name':'Rema',
            'mark':81,
        },
        '29':{
            'id':'29',
            'Name':'Sanga',
            'mark':47,
        },
        '30':{
            'id':'30',
            'Name':'Dari',
            'mark':23,
        },
    };

JS code to create the table data:

const markObtained = Object.keys(students).reduce(function(names, name) {
if (students[name].mark>=20) {
    names[name] = students[name];
}
return names;
}, {});
const doc = document.createDocumentFragment();
const table = document.querySelector("table");
for(let i=0; i<Object.keys(markObtained).length; i++) {
    const data = Object.values(markObtained)[i];
    const tr = document.createElement("tr");
    for (const v in data){
         const td = document.createElement("td");
        td.textContent = data[v];
        tr.appendChild(td);
    }
  table.appendChild(tr);
 }
table.appendChild(doc);

I would highly appreciate if any expert in js can help me better optimize the codes and give me another workaround examples?

\$\endgroup\$
2
\$\begingroup\$

Minor problems and style points

  • The table has named columns set out already. This is problematic as the for in loop is alphabetic and thus you would be adding Name, id, mark which does not match the column headings.

    It would be best to have the student fields defined as an array in the order that they are to be displayed. This can either be as dataset properties of the array or in the source code, or added to the dataset. The example has it in the source (be cause I am lazy)

  • Use for of rather than for(;;) and try to avoid ever using for in as it has a long list of caveats and problems associated with its use.

  • The function markObtained...

    • ...seems redundant in the example you give. The data can be accessed just as easily in one pass.

    • ...the naming is confusing withing the reduce call. names, and name are both referring to the student id. This is especially problematic as the dataset has a field called Name (which should not be capitalized)

    • ...creates an object indexed by id, the dataset you have shown is also an Object. Both are indexed via student id, yet you don't ever use the student id to lookup a student. markObtained would be best to return an array of student id's rather than copy the student references to a new object. See Example A

  • The document fragment your create is not used, the last line is adding an empty fragment to the table.

  • A table has insertRow(), and rows have insertCell(), use them to add to the table rather than create them as DOM element via document.createElement

Extract ids

From above points regarding the function markObtained The example creates an array of student ids that can be used to locate students in the original dataset. Also renames the variables so not to be confusing.

Example A

const markObtained = Object.keys(students).reduce(function(ids, id) {
    if (students[id].mark>=20) { ids[id] = id }
    return ids;
}, []);

Adding to DOM

You are appending to the table that is on the page. Each time you add a cell or row you are forcing a re flow.

You can either create the whole table in code and add it to the DOM when ready, or you can remove the table from the DOM, modify it and put it back see Example B.

If you remove and then put back it would be best to put the table within a container to ensure it goes where it belongs without undue fuss.

Example B

const students = {
    "24": {id: 24, name: "Zuali", mark: 30},
    "25": {id: 25, name: "Famkima", mark: 52},
    "27": {id: 27, name: "Duha", mark: 77},
    "28": {id: 28, name: "Rema", mark: 81},
    "29": {id: 29, name: "Sanga", mark: 47},
    "30": {id: 30, name: "Dari", mark: 23},
};

const PASS_MARK = 20;
const FIELDS = ["id", "name", "mark"];
updateStudentsTable(students);
 
function addPassed(table, students, pass = PASS_MARK) {
    for (const student of students) {
        if (student.mark >= pass) {
            const row = table.insertRow();
            for (const field of FIELDS) {
                row.insertCell().textContent = student[field];
            }
        }
    }
}
function updateStudentsTable(students) {
    const table = document.querySelector("table");
    const par = table.parentElement;
    par.removeChild(table);
    addPassed(table, Object.values(students));
    par.appendChild(table);
}
<div>
    <table>
        <thead>
            <tr>
                <th>Id</th><th>Name</th><th>Mark</th>
            </tr>
        </thead>
    </table>
</div>

\$\endgroup\$
1
\$\begingroup\$

Convert key-value objects to array

The way you traverse your objects is convoluted. Calling Object.keys() and Object.values() all over the place like for(let i=0; i<Object.keys(markObtained).length; i++) and const data = Object.values(markObtained)[i]; can easily be avoided if you transform your input into an array.

// input = your original students object
const students = Array.from(Object.keys(input), k => input[k]);

The advantage is you can use built-in methods to filter, map, reduce the array.

if (students[name].mark>=20) {
    names[name] = students[name];
}

can be replaced with..

students.filter(x => x.mark >= 20).map(x => x.Name);
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.