Looping through nested arrays and converting to objects

14,771

Solution 1

What's wrong with your code:

The third level for loop is messed up. It should be removed:

for (var y = 0; y < employeeData[i][x].length; y++) {
//                                  ^ by the way this should be x not y (not fixing the problem though)

because the third level arrays contain 2 elements that you need to use at the same time (as key-value), the for loop for them should be removed.

Fix:

for (var i = 0; i < employeeData.length; i++) { 
    for (var x = 0; x < employeeData[i].length; x++) { 
        newObject[employeeData[i][x][0]] = employeeData[i][x][1];
    }
    newArr.push(newObject);
    newObject = {};
}

Fixed code example:

var employeeData = [
  [
    ['firstName', 'Bob'], ['lastName', 'Lob'], ['age', 22], ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary'], ['lastName', 'Joe'], ['age', 32], ['role', 'director']
  ]
]


function transformData(employeeData) {
  let newObject = {};
  let newArr = [];

  for (var i = 0; i < employeeData.length; i++) { 
    for (var x = 0; x < employeeData[i].length; x++) { 
        newObject[employeeData[i][x][0]] = employeeData[i][x][1];
    }
    newArr.push(newObject);
    newObject = {};
  }
  return newArr;
}

console.log(transformData(employeeData));

Alternative solution:

You can map employeeData array into a new array, reduceing every sub-array into an object like this:

var result = employeeData.map(function(sub) {
    return sub.reduce(function(obj, pair) {
        obj[ pair[0] ] = pair[1];
        return obj;
    }, {});
});

Which can be shortened using ES6's arrow functions to:

let result = employeeData.map(sub => sub.reduce((obj, pair) => (obj[pair[0]] = pair[1], obj), {}));

Example:

let employeeData = [
  [
    ['firstName', 'Bob'], ['lastName', 'Lob'], ['age', 22], ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary'], ['lastName', 'Joe'], ['age', 32], ['role', 'director']
  ]
];

let result = employeeData.map(sub => sub.reduce((obj, pair) => (obj[pair[0]] = pair[1], obj), {}));

console.log(result);

Solution 2

How to fix your code

You only need 2 for loops: 1. iterate the array 2. iterate the sub arrays and construct the object

var employeeData = [[["firstName","Bob"],["lastName","Lob"],["age",22],["role","salesperson"]],[["firstName","Mary"],["lastName","Joe"],["age",32],["role","director"]]]

function transformData(employeeData) {
  let newObject;
  const newArr = [];

  for (var i = 0; i < employeeData.length; i++) {
    newObject = {}; // init new object
    for (var x = 0; x < employeeData[i].length; x++) {
        newObject[employeeData[i][x][0]] = employeeData[i][x][1]; // iterate inner arrays and assign properties to object
    }
    newArr.push(newObject);
  }
  return newArr;
}

console.log(transformData(employeeData));

Another option is to use a combination of Array#map to iterate the outer array and Array#reduce to construct an object from the inner arrays:

const employeeData = [[["firstName","Bob"],["lastName","Lob"],["age",22],["role","salesperson"]],[["firstName","Mary"],["lastName","Joe"],["age",32],["role","director"]]]

const result = employeeData.map((arr) => 
  arr.reduce((o, [key, value]) => (o[key] = value, o), {})
);

console.log(result);

Solution 3

The issue is your use of the variables x and y

For one thing, there's the line

for (var y = 0; y < employeeData[i][y].length; y++)

Perhaps you meant instead to use employeeData[i][x].length, because as you have it here, it is going to behave very strangely.

However, you can entirely eliminate the variable y if you replace it with x (which, in your implementation is never even used)

Here's my suggested edits to your function:

function transformData(employeeData) {
  let newObject = {}; 
  let newArr = []; 

  for (var i = 0; i < employeeData.length; i++) { 
    for (var x = 0; x < employeeData[i].length; x++) { 
      newObject[employeeData[i][x][0]] = employeeData[i][x][1];
    }   
    newArr.push(newObject);
    newObject = {}; 
  }
  return newArr;
}

Running your example with these changes I got correct output:

[
  {
    firstName: 'Bob',
    lastName: 'Lob',
    age: 22,
    role: 'salesperson'
  },
  {
    firstName: 'Mary',
    lastName: 'Joe',
    age: 32,
    role: 'director'
  }
]

Solution 4

This is quite easy to do in a single line. Javascript has a function just for this.

const employeeData = [
  [
    ['firstName', 'Bob'], ['lastName', 'Lob'], ['age', 22], ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary'], ['lastName', 'Joe'], ['age', 32], ['role', 'director']
  ]
]

const res = employeeData.map(data => Object.fromEntries(data))
console.log(res)

Cheers

Solution 5

The problmem you face can be solved using for loops, as you were trying, if you use your indexes correctly. If you format your data as I did, you will see that there is three levels for your indexes your [i,x,y];

for example for employeeData[0] you should get:

[
    ['firstName', 'Bob'],
    ['lastName', 'Lob'],
    ['age', 22],
    ['role', 'salesperson']
  ]

then for employeeData[0][0] you should get:

 ['firstName', 'Bob']

and for employeeData[0][0][0] you should get: 'firstName'

To access 'Bob' you would need to employeeData[0][0][1] and since you know that there is only two elements in this inner array you don´t need to loop though it. as @TallChuck suggested great part of your problem stems from forgetting to use your x index.

var employeeData = [
  [
    ['firstName', 'Bob'],
    ['lastName', 'Lob'],
    ['age', 22],
    ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary'],
    ['lastName', 'Joe'],
    ['age', 32],
    ['role', 'director']
  ]
]


function transformData(employeeData) {
  let newObject = {};
  let newArr = [];

  for (var i = 0; i < employeeData.length; i++) {
    for (var x = 0; x < employeeData[i].length; x++) {
      newObject[employeeData[i][x][0]] = employeeData[i][x][1];
    }
    newArr.push(newObject);
    newObject = {};
  }
  return newArr;
}

console.log(transformData(employeeData));

EDIT


You could also make some more complex solutions if you pay attention to your indexes. Say you have the following data:

var employeeData = [
  [
    ['firstName', 'Bob', 'weight', '80kg'],
    ['lastName', 'Lob'],
    ['age', 22],
    ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary', 'eye color', 'green'],
    ['lastName', 'Joe'],
    ['age', 32],
    ['role', 'director']
  ]
]

Then the solution I gave before wouldn´t work directly. But if you look closely you will see that in some of the arrays your field names are located in the positions 0, 2 of the Y index. Which means that your field names are in a pair positions and the filed values in the odd positions. So you can actually make a loop through y and just check if the Y index is divisible by 2.

if(y % 2 == 0 ..){}

And do this only if there is an accompanying odd value thus

if(y % 2 == 0 && employeeData[i][x][y+1]){..}

The full code is below.

var employeeData = [
  [
    ['firstName', 'Bob', 'weight', '80kg'],
    ['lastName', 'Lob'],
    ['age', 22],
    ['role', 'salesperson']
  ],
  [
    ['firstName', 'Mary', 'eye color', 'green'],
    ['lastName', 'Joe'],
    ['age', 32],
    ['role', 'director']
  ]
]


function transformData(employeeData) {
  let newObject = {};
  let newArr = [];

  for (var i = 0; i < employeeData.length; i++) {
    for (var x = 0; x < employeeData[i].length; x++) {
      for (var y = 0; y < employeeData[i][x].length; y++) {
        if(y % 2 == 0 && employeeData[i][x][y+1]){
          newObject[employeeData[i][x][y]] = employeeData[i][x][y+1];
        }
      }
    }
    newArr.push(newObject);
    newObject = {};
  }
  return newArr;
}

console.log(transformData(employeeData));

Share:
14,771
knox-flaneur
Author by

knox-flaneur

Updated on June 13, 2022

Comments

  • knox-flaneur
    knox-flaneur almost 2 years

    I want to convert an group of nested arrays into an array of objects with the collected information from the nested arrays:

    BEFORE:

    var employeeData = [
      [
        ['firstName', 'Bob'], ['lastName', 'Lob'], ['age', 22], ['role', 'salesperson']
      ],
      [
        ['firstName', 'Mary'], ['lastName', 'Joe'], ['age', 32], ['role', 'director']
      ]
    ]
    

    AFTER:

    [
      {firstName: 'Bob', lastName: 'Lob', age: 22, role: 'salesperson'},
      {firstName: 'Mary', lastName: 'Joe', age: 32, role: 'director'}
    ]
    

    Here is the function I wrote to solve this but I can't quite see where the loop is going wrong:

        var employeeData = [
          [
            ['firstName', 'Bob'], ['lastName', 'Lob'], ['age', 22], ['role', 'salesperson']
          ],
          [
            ['firstName', 'Mary'], ['lastName', 'Joe'], ['age', 32], ['role', 'director']
          ]
        ]
    
    
        function transformData(employeeData) {
          let newObject = {};
          let newArr = [];
      
          for (var i = 0; i < employeeData.length; i++) { 
            for (var x = 0; x < employeeData[i].length; x++) { 
              for (var y = 0; y < employeeData[i][y].length; y++) { 
                newObject[employeeData[i][y][0]] = employeeData[i][y][1];
              } 
            }
            newArr.push(newObject);
            newObject = {};
          }
          return newArr;
        }
        
        console.log(transformData(employeeData));

    Thanks in advance.

    • takendarkk
      takendarkk over 6 years
      "I can't quite see where the loop is going wrong" I cannot either because I do not know what your code is producing. Can you give an example of what your output looks like now?
    • TallChuck
      TallChuck over 6 years
      I notice that you never use the variable x
    • Scott Marcus
      Scott Marcus over 6 years
      As you can see after editing the question to include a code snippet, the code does work, you are simply only working with the first two array elements and need to expand it to include all the data.
  • Admin
    Admin over 6 years
    @ScottMarcus: You're right. I'll add further info to go from the Map to the Object as requested.
  • Scott Marcus
    Scott Marcus over 6 years
    Actually, it's not. The code works just fine, it just doesn't go as deep into the nested arrays as needed.
  • Scott Marcus
    Scott Marcus over 6 years
    The question wasn't about map. The question was what's wrong with the loop that the OP is using. And, the answer is nothing. The OP just doesn't extract all the data that is available that's all.
  • TallChuck
    TallChuck over 6 years
    @ScottMarcus that's because your y loop is using employeeData[i][y] instead of employeeData[i][x]
  • Scott Marcus
    Scott Marcus over 6 years
    It's not my loops at all and your results and explanation are incorrect.
  • Carl Edwards
    Carl Edwards over 6 years
    In your reduce call what exactly are you returning in the parenthesis: (obj[pair[0]] = pair[1], obj)? More particularly the obj.
  • ibrahim mahrir
    ibrahim mahrir over 6 years
    @CarlEdwards That using a comma operator to combine two statement in one. Isn't that obvious from ES5 version of the code?
  • Admin
    Admin over 6 years
    @ScottMarcus: Yep, I know. I'm just offering an alternative that he may or may not decide to accept as a solution to his broken code. Other answers have handled the direct issue.
  • ibrahim mahrir
    ibrahim mahrir over 6 years
    @CarlEdwards MDN docs for the comma operator: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….
  • Scott Marcus
    Scott Marcus over 6 years
    That's not what a good answer should do. Answer the question first, then offer alternatives.
  • TallChuck
    TallChuck over 6 years
    @ScottMarcus ah excuse me, yes, I accidentally had [i][y][0] where I should have had [i][y][1], it works correctly now if you would just give it a try
  • Admin
    Admin over 6 years
    @ScottMarcus: I don't want to repeat what others wrote. Maybe it isn't a good answer, but it's helpful information.
  • Scott Marcus
    Scott Marcus over 6 years
    Then it should be a comment, not an answer.
  • Admin
    Admin over 6 years
    @ScottMarcus: I don't think it would help the asker to try to squeeze all that into a comment. We are ultimately here to be helpful. This isn't a game show. It's real people with real problems, and different solutions to a problem can enhance a user's knowledge.
  • TallChuck
    TallChuck over 6 years
    This is a very specifically tailored 'fix' that won't work with arrays of any size other than 4
  • Carl Edwards
    Carl Edwards over 6 years
    Thanks. Never knew you could do something like this.
  • Scott Marcus
    Scott Marcus over 6 years
    @ibrahimmahrir Yes, I am. The second loop was unnecessary, but wasn't affecting the result. The third loop is right.
  • Scott Marcus
    Scott Marcus over 6 years
    @TallChuck True, but I am answering the specific question that is being asked. I'm not trying to provide a better solution because (as I said in my answer) that has already been provided by others.
  • Scott Marcus
    Scott Marcus over 6 years
    You could make a Fiddle and just post a link to it in a comment.
  • Admin
    Admin over 6 years
    @ScottMarcus: That's true, but broken links, and so on. Seems more helpful to have the code right here in a live demo.
  • James
    James over 6 years
    The main problem in the question is that for (var y = 0; y < employeeData[i][y].length; y++) doesn't produce a standard loop, rather it checks the length of each y instead of the level prior. Your answer doesn't address that, and in fact you have the same problem here too. Consider inside the second for loop using y < employeeData[i].length
  • Scott Marcus
    Scott Marcus over 6 years
    Not when the answer doesn't actually answer the question. Look, I'm just trying to help you understand what a good answer is on SO. And, rule #1 is to actually answer the question. If you aren't going to do that, then you aren't providing an answer.
  • Admin
    Admin over 6 years
    @ScottMarcus: Yeah, I'm probably just bad at giving good answers according to the strictest definition herein. I end up leaning more toward pragmatism.
  • Scott Marcus
    Scott Marcus over 6 years
    @James As I've said (a number of times at this point), I am working with the code that the OP provided and explaining where (that ill-advised) approach falls short. You can see for yourself that the code does, in fact, work with my modifications.
  • Scott Marcus
    Scott Marcus over 6 years
    I think you'll find it's not that strict of a definition. You either address the question asked by the OP or you don't.
  • Admin
    Admin over 6 years
    @ScottMarcus: Isn't that strict? My idea of a way to answer is far more loose and interpretive. I want to read more into their ultimate needs. They're a lost soul crying out for help and I wish to answer that silent, desperate call. I want to get deep into their heads. Like a benevolent Hannibal Lecter.
  • Scott Marcus
    Scott Marcus over 6 years
    No, it's not strict, just address the specific question being asked first and then supply alternatives second. You just provided the alternatives. With that kind of approach, the OP may solve the problem, but not learn anything about the specifics of what they were attempting. Your "answer" doesn't "answer" anything about the loops being used.
  • TallChuck
    TallChuck over 6 years
    @ScottMarcus you are not correcting his code, you are simply giving him an answer that happens to give the right solution in a single test case, but the reason your code works is not because it is correct, it is because you wrote it for the test case. If you used any example longer or shorter than the example he gave, it would not work, because your code does not address the issue
  • Scott Marcus
    Scott Marcus over 6 years
    @TallChuck I disagree. The OPs results simply didn't include all of the data and I explained that. Yes, the code is brittle, no one is debating that, but that is not the point. The reason the OPs results weren't what they wanted is because the data needed to be looked at more deeply than was being done. I wanted to show that very point and that's why I answered the way I did. I was very explicit that this was not the best way to solve the issue.
  • knox-flaneur
    knox-flaneur over 6 years
    @ibrahimmahrir Thanks! To articulate the mistake in my reasoning, I think I saw each of the two elements (e.g., first and Bob) as something to "loop through" which means to work on individually. Only the second for loop was required since I want to work on those two key-value pairs as a set in one iteration, rather than each element ("first" and then "Bob") having its own iteration.
  • TallChuck
    TallChuck over 6 years
    @ScottMarcus you didn't answer his question because you didn't address where the loop is going wrong
  • Scott Marcus
    Scott Marcus over 6 years
    @TallChuck The loop isn't going wrong as my results show. It's just not scalable.
  • Admin
    Admin over 6 years
    @ScottMarcus: Yeah, like I said, others have that covered. No sense echoing that same information. I'm happy with the information provided.
  • Scott Marcus
    Scott Marcus over 6 years
    That's the point. If others answered the question, then you shouldn't.
  • Admin
    Admin over 6 years
    @ScottMarcus: I like to rock the boat.
  • knox-flaneur
    knox-flaneur over 6 years
    @ibrahimmahrir when people talk about "map reduce" is this most often in regards to nested arrays?
  • ibrahim mahrir
    ibrahim mahrir over 6 years
    @knox-flaneur Usually but not necessarly all nested arrays need a map/reduce. It really depends on what you're trying to do with your data. Imagine a nested array like this: [ ["knox", "flaneur"], ["ibrahim", "mahrir"]] and you want to change to ["knox flaneur", "ibrahim mahrir"], then only map is used with join: àrr.map(sub => sub.join(" "));. If the array is [ [5, 6, 7], [1, 4, 2] ] and you want to transform it into [18, 7], then map/reduce is useful: arr.map( sub => sub.reduce((sum, n) => sum + n, 0) );. See? It really depends on your what you're trying to do with data
  • ibrahim mahrir
    ibrahim mahrir over 6 years
    @knox-flaneur But I say in most cases map/reduce is used for nested arrays. You map elements of the array, and for each element you use reduce on them, which implies (they have something to do with arrays, either they are arrays, or they are some other kind of objects that contain arrays in them).