Looping through nested arrays and converting to objects
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, reduce
ing 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));
knox-flaneur
Updated on June 13, 2022Comments
-
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 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 over 6 yearsI notice that you never use the variable x
-
Scott Marcus over 6 yearsAs 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 over 6 years@ScottMarcus: You're right. I'll add further info to go from the
Map
to theObject
as requested. -
Scott Marcus over 6 yearsActually, it's not. The code works just fine, it just doesn't go as deep into the nested arrays as needed.
-
Scott Marcus over 6 yearsThe 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 over 6 years@ScottMarcus that's because your
y
loop is usingemployeeData[i][y]
instead ofemployeeData[i][x]
-
Scott Marcus over 6 yearsIt's not my loops at all and your results and explanation are incorrect.
-
Carl Edwards over 6 yearsIn your
reduce
call what exactly are you returning in the parenthesis:(obj[pair[0]] = pair[1], obj)
? More particularly theobj
. -
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 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 over 6 years@CarlEdwards MDN docs for the comma operator: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/….
-
Scott Marcus over 6 yearsThat's not what a good answer should do. Answer the question first, then offer alternatives.
-
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 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 over 6 yearsThen it should be a comment, not an answer.
-
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 over 6 yearsThis is a very specifically tailored 'fix' that won't work with arrays of any size other than 4
-
Carl Edwards over 6 yearsThanks. Never knew you could do something like this.
-
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 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 over 6 yearsYou could make a Fiddle and just post a link to it in a comment.
-
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 over 6 yearsThe 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 eachy
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 usingy < employeeData[i].length
-
Scott Marcus over 6 yearsNot 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 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 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 over 6 yearsI 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 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 over 6 yearsNo, 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 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 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 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 over 6 years@ScottMarcus you didn't answer his question because you didn't address where the loop is going wrong
-
Scott Marcus over 6 years@TallChuck The loop isn't going wrong as my results show. It's just not scalable.
-
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 over 6 yearsThat's the point. If others answered the question, then you shouldn't.
-
Admin over 6 years@ScottMarcus: I like to rock the boat.
-
knox-flaneur over 6 years@ibrahimmahrir when people talk about "map reduce" is this most often in regards to nested arrays?
-
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 onlymap
is used withjoin
:àrr.map(sub => sub.join(" "));
. If the array is[ [5, 6, 7], [1, 4, 2] ]
and you want to transform it into[18, 7]
, thenmap/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 over 6 years@knox-flaneur But I say in most cases
map/reduce
is used for nested arrays. Youmap
elements of the array, and for each element you usereduce
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).