-1

I don't wish to use JQuery, just to put that upfront.

I have a template for data. This template will be used for rendering fields of data (or not as the case may be). I've sorted out the matching part of the objects. The problem I have is when it comes to iterating over the array of objects, and matching said objects to the template. The results I get all have the same value as the last object in the incoming array.

//template
//actual template/incoming data is much more than this, but im keeping it a simple example

const template = {"firstname":null, "lastname":null, "dob":null}

//incoming (from a fileupload, but i will put it here an example)
const incoming = [{"firstname":"John", "dob":"1996-04-03"},
                  {"firstname":"Jill", "lastname":"Smith", "dob":"1986-09-17"}]

function readFile() { // function read the files from upload
    const fileIn = document.querySelector('#file-input'); //finds the input area in the form
    var reader = new FileReader();
    reader.onload = onReaderLoad;
    reader.readAsText(filein.files[0]); //gets the uploaded json file
};

function onReaderLoad(event) {
    const obj = JSON.parse(event.target.result); //parse the uploaded file
    const matched = []; //empty array to push matched objects to

    obj.forEach(el => {
        //i left this function out of this example because it works as expected, returns 1 object
        matched.push(cleanData(el)); //call the function that matches the template and incoming
    });
    console.log(matched)
};

document.getElementById('file-input).addEventListener('change', onChange);

when I this, the result puts out a 2-item array with all results matching the data of the "Jill" object.

[{"firstname":"Jill", "lastname":"Smith", "dob":"1986-09-17"},
{"firstname":"Jill", "lastname":"Smith", "dob":"1986-09-17"}]

what I am expecting is:

[{"firstname":"John", "lastname":null, "dob":"1996-04-03"},
{"firstname":"Jill", "lastname":"Smith", "dob":"1986-09-17"}]

My expectation, is that in the forEach call, it should push a new object with matched fields to the empty matched array, however, it doesn't appear to be working that way. Am I misunderstanding something? I come from a python background, so javascript isn't my first language and I tend to think with a python brain.

this is the cleanData() function:

function cleanData(d1, d2=template) {
    let _copy = Object.assign({}, d2) //makes a copy
    const commonKeys = getKeys(d1, _copy) //returns a list of keys found in both


    commonKeys.forEach(element => {

        //is it an object, but not array?
        if((d1[element] instanceof Object) && (Array.isArray(d1[element]))){
             const subKeys = getKeys(d1[element], _copy[element]);

             subKeys.forEach(el => {
                 _copy[element][el] = d1[element][el];});

        }else if(Array.isArray(d1[element])){ //is it an array?
             var objList = [];

             for(var i=0; i<d1[element].length; ++i){
                 if((d1[element] instanceof Object) && (Array.isArray(d1[element]))){
                     const temp=d1[element][i];
                     //array in _copy will always be length 0
                     const temp_fix = cleanData(temp, _copy[element][0])

                     objList.push(temp_fix);
                     _copy[element] =  objList;
                 }else { _copy[element] = d1[element]}
             }
        } else { //if not array or obj
              _copy[element] = d1[element]
        }
     });
    return _copy;
}
nos codemos
  • 569
  • 1
  • 5
  • 19
  • 2
    A lot depends on what that `cleanData` function does... BTW, you may be better off doing `matched = obj.map(el => cleanData(el))` – Heretic Monkey Nov 15 '21 at 15:05
  • cleanData takes the incoming JSON object and matches the keys to the template, and returns a new object with the partially or wholly matched fields. – nos codemos Nov 15 '21 at 15:09
  • @noscodemos well, if it did, you wouldn't be asking this question, right? Without knowing *how* `cleanData` works, we can't tell you how to fix it. – VLAZ Nov 15 '21 at 15:10
  • 1
    You've said what it does. *How* it does that is what matters. The way JavaScript works with references matters. – Heretic Monkey Nov 15 '21 at 15:10
  • i ill edit it in. give me a few mins. – nos codemos Nov 15 '21 at 15:11
  • 2
    Most likely your `cleanData` does something like `Object.assign(template, otherObject)` which *mutates* the template. See [A javascript design pattern for options with default values?](https://stackoverflow.com/q/9602449) | [JavaScript equivalent of jQuery's extend method](https://stackoverflow.com/q/11197247) | [How do I correctly clone a JavaScript object?](https://stackoverflow.com/q/728360) | [Is this a good way to clone an object in ES6?](https://stackoverflow.com/q/39736397) – VLAZ Nov 15 '21 at 15:20
  • hey how did you know that's exactly what i did. – nos codemos Nov 15 '21 at 15:21
  • 1
    "_hey how did you know that_" - because @VLAZ is a hero! :-) – Randy Casburn Nov 15 '21 at 15:24
  • Because you were mutating the `template`. I just wasn't sure what exact code you had that does it. However, your code actually has `let _copy = Object.assign({}, d2)` which *does* make a copy. So it should work. Do note that it only does a shallow copy, so you shouldn't experience an issue *with this template* example: https://jsbin.com/kulavefahe/1/edit?js,console . You would have a problem if you mutate nested keys. – VLAZ Nov 15 '21 at 15:33
  • Yeah, it is nested, which is probably where the encounter came from. I provided an answer where I changed it to JSON.parse(...) and it now produces the results I expected. – nos codemos Nov 15 '21 at 15:39

1 Answers1

0

So thank you to @VLAZ linking those SO pages. It was a problem in the cleanData function as (s)he so presciently pointed out. I replaced the let _copy = Object.assign({}, d2) line with let _copy = JSON.parse(JSON.stringify(d2)) which gave me the output I expected. I used this rather than a very fancy deep copy method, because in the template it will only have the dtypes Object, Array, String, or Integer.

nos codemos
  • 569
  • 1
  • 5
  • 19