Array manipulation - build a new array based on the input from 3 arrays

Hello

I have 2 arrays returned from the two SQL servers. I would like to build a new array I will feed to the dashboard table which will show the comparison between result of the queries.

I get to the point where I create a 3rd array which is a list of results without duplicates and two arrays formatted to the same structure.

Code below from the moment when 3 arrays are formatted:

var arr = [];
var obj_arr = [{"process":"A"},{"process":"B"},{"process":"C"},{"process":"D"}];
var arr1 = [{"process":"C","qty":88,"origin":"MMS"},{"process":"B","qty":11,"origin":"MMS"},{"process":"A","qty":8,"origin":"MMS"}]
var arr1_length = arr1.length;
var arr2 = [{"process":"A","qty":280,"origin":"SP"},{"process":"D","qty":6,"origin":"SP"},{"process":"C","qty":9,"origin":"SP"}]
var arr2_length = arr2.length;

//Sort arr1 (results are sorted on SQL, but I want this to be future proof)
arr1.sort(function(a, b){
  var x = a.process.toLowerCase();
  var y = b.process.toLowerCase();
  if (x < y) {return -1;}
  if (x > y) {return 1;}
  return 0;
});

//Sort arr2
arr2.sort(function(a, b){
  var x = a.process.toLowerCase();
  var y = b.process.toLowerCase();
  if (x < y) {return -1;}
  if (x > y) {return 1;}
  return 0;
});

//assign values from arr1 to "processes" from obj_arr
var arr3 = obj_arr.map((item, i) => Object.assign({}, item, arr1[i]));
//truncate to arr1_length
arr3 = arr3.slice(0,arr1_length);

//assign values from arr2 to "processes" from obj_arr
var arr4 = obj_arr.map((item, i) => Object.assign({}, item, arr2[i]));
//truncate to arr2_length
arr4 = arr4.slice(0,arr2_length);

obj_arr.forEach(BuildList); //build list of processes

var process;
var arr3qty;
var arr4qty;
var z = 0;

function BuildList (obj_arr,z,arr3,arr4) {
    for (i=0; i < obj_arr.length; i++) {
        if (obj_arr.process == arr3.process){
            arr3qty = arr3.qty;
        } else {
            arr3qty = "test1" // change to null after tetsing;
          }
        if (obj_arr.process == arr4.process){
            arr4qty = arr4.qty;
        } else {
            arr34ty = "test2" // change to null after tetsing;
          }
    }
    process = obj_arr.process;
    arr[z]=[z+1,process, arr3qty, arr4qty];               // build an array
    z = z+1;
    msg.payload = arr;
}

/*
Expected Structure: 
[[1,"A",arr3.qty, arr4.qty],[2,"B",arr3.qty, arr4.qty],[3,"C",arr3.qty, arr4.qty],[4,"D",arr3.qty, arr4.qty]]
Expected Output:
[[1,"A",8,280],[2,"B",11,null],[3,"C",88,9],[4,"D",null,6]]
Actual Output:
[[1,"A",null,null],[2,"B",null,null],[3,"C",null,null],[4,"D",null,null]]
*/

msg.obj_arr = obj_arr;
msg.arr1 = arr1;
msg.arr2 = arr2;
msg.arr3 = arr3;
msg.arr4 = arr4;
return msg;

Please can somebody help me with getting function BuildList working.
What I'm trying to achieve there is to use obj_arr as a list, which will finally be displayed in the 2nd column of the table and assign corresponding values from the arr3 and arr4.

I know that the if else statements of the function doesn't work, but I don't know why.

I should mention I'm fairly new to programming.

Example of the table I want to build:
image

Make 1 array - then sort.

E.g.

var arrays = [...arr1, ...arr2];
arrays.sort(function(a, b){
  var x = a.process.toLowerCase();
  var y = b.process.toLowerCase();
  if (x < y) {return -1;}
  if (x > y) {return 1;}
  return 0;
});

Hello

Thank you for your response, but proposed solution is not giving me a structure I'm looking for.
It leaves me with the same problem that I have to loop through the 1st array to make a list and then loop through 2nd array to assign values to the list.

Your solution output:

[{"process":"A","qty":8,"origin":"MMS"},{"process":"A","qty":280,"origin":"SP"},{"process":"B","qty":11,"origin":"MMS"},{"process":"C","qty":88,"origin":"MMS"},{"process":"C","qty":9,"origin":"SP"},{"process":"D","qty":6,"origin":"SP"}]

Yeah, i didnt fully read your post (I assumed you had the final formatting & was demonstrating a simple means of merging)

No matter - this is one (not so elegant) solution...
NOTE: rather than sort, I use your first array in obj_arr to position the final array elements. Means you can order them in any format you wish & also, as a bonus, it filters out any process NOT in that array.


var obj_arr = [{"process":"A"},{"process":"B"},{"process":"C"},{"process":"D"}];
var arr1 = [{"process":"C","qty":88,"origin":"MMS"},{"process":"B","qty":11,"origin":"MMS"},{"process":"A","qty":8,"origin":"MMS"}]
var arr2 = [{"process":"A","qty":280,"origin":"SP"},{"process":"D","qty":6,"origin":"SP"},{"process":"C","qty":9,"origin":"SP"}]

/** convert array to object using key as the - key! */
const arrayToObject = (array, key) => {
  const initialValue = {};
  return array.reduce((obj, item) => {
    item.SP = item.origin == "SP" ? item.qty : null; //set this items SP to qty if the process=="SP"
    item.MMS = item.origin == "MMS" ? item.qty : null; //set this items MMS to qty if the process=="MMS"
    return {
      ...obj,
      [item[key]]: item,
    };

  }, initialValue);
};

/** merge 2 objects recursively */
const merge = (obj1,obj2) => {
    var obj3 = {};
    for (var attrname in obj1) { 
        obj3[attrname] = obj1[attrname]; 
    }
    for (var attrname in obj2) {
        if(obj3[attrname] != null && typeof obj2[attrname] === "object" && typeof obj3[attrname] === "object"){
            obj3[attrname] = merge(obj3[attrname], obj2[attrname]);
        }
        if(obj3[attrname] == null){
            obj3[attrname] = obj2[attrname];
        }  
    }
    return obj3;
};

msg.arr1 = arr1;//add to msg to view in debug output
msg.arr2 = arr2;//add to msg to view in debug output
var arr1obj = arrayToObject(arr1, "process" ); //convert the array to object using 'process' as the key
var arr2obj = arrayToObject(arr2, "process" ); //convert the array to object using 'process' as the key

msg.arr1obj = arr1obj; //add to msg to view in debug output 
msg.arr2obj = arr2obj; //add to msg to view in debug output

//next merge the 2 obexts
msg.merged = merge(msg.arr1obj,msg.arr2obj);

//lastly, generate an array from the merged object
msg.finalArray = [];
//sort and filter based on objects in obj_arr
for(let x = 0; x < obj_arr.length; x++){
    const process = obj_arr[x].process;
    let item = msg.merged[process];
    if(item){
        let el = [
             msg.finalArray.length+1, //index
             process,// A/B/C/D etc
             item.MMS, // MMS qty
             item.SP //SP qty
        ];
        msg.finalArray.push(el)    
    }
}

return msg;

/*
Expected Structure: 
[[1,"A",arr3.qty, arr4.qty],[2,"B",arr3.qty, arr4.qty],[3,"C",arr3.qty, arr4.qty],[4,"D",arr3.qty, arr4.qty]]
Expected Output:
[[1,"A",8,280],
 [2,"B",11,null],
 [3,"C",88,9],
 [4,"D",null,6]]
 Actual Output:
 [[1,"A",8,280],
  [2,"B",11,null],
  [3,"C",88,9],
  [4,"D",null,6]]
*/

Proof...

1 Like

This is working great.
Thank you for your help.

Please can you explain how the part of the code commented /** merge 2 objects recursively */ works.
My understanding is that you set const merge and then create function using => notation. Function accepts obj1, obj2 as arguments and return obj3. Than you declare obj3 as and object within function scope.
I don't know what attrname is and why for (var attrname in obj1) and for (var attrname in obj2) will return true and what obj1[attrname] will look like.
I searched for attrname and the closest find was about the .attr() function which returns attribute value, but the syntax is different. I will appreciate if you can point me in the right direction to understand this part.

Recursion is a common technique for scaning nested objects. since an array is an object and your original arrays held several objects it was necessary to recursively scan the array in order to turn it from an array of objects into an object of objects.

recursion is quite difficult to explain and quite difficult to visualise but essentially the recursive function calls itself until it has completely scanned all objects and child objects.

A little bit like inception :wink:

Firstly, attrname is just a local made up variable.

Essentially for (var attrname in obj1) runs through the properties & retuns each key e.g. "process" in attrname then "qty" in attrname etc.

This allows me to then access the child object properties dynamically, (by name) using square bracket notation

E.g in a loop I can't use hard coded references like obj1.process or obj2.qty I need to get the child properties specified in the value of attrName
Using obj1[attrname] is how we do this in JavaScript.

This is known as property access, square bracket notation. Here is a decent explanation.

If you want to fully understand the code, look at the various objects stored in the msg to see how each stage of the code transforms your data up to the point finalArray is created.

Hope that helps.

Ps, I haven't proof read the above as I'm on mobile - hopefully it makes some sense.

Should read a bit better now :slight_smile:

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.