Mangled_Aura_Nada
Mangled_Aura_Nada

Reputation: 147

How to iterate through a nested object and putting values into an array?

I created a nested object that holds the following format:

var lineMap = {
  2016: {
    Hemoglobin: 33 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 13 ,
    Cytochrome C: 33
  },
  2017: {
    Hemoglobin: 8 ,
    Lysozyme: 47 ,
    Myoglobin: 8 ,
    DNA: 12 ,
    Cytochrome C: 33
  },
  2018: {
    Hemoglobin: 8 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 12 ,
    Cytochrome C: 13
  },
  2019: {
    Hemoglobin: 8 ,
    Lysozyme: 8 ,
    Myoglobin: 47 ,
    DNA: 8 ,
    Cytochrome C: 47
  }
}

And I'd like to place each year's item count into its own index of an array such that:

var arr = [
  [33, 33, 47, 13, 33],
  [8, 47, 8, 12, 33],
  [8, 33, 47, 12, 13],
  [8, 8, 47, 8, 47]
]

I have tried with creating a nested for loop to iterate through the nested object lineMap.

for (var i = 0; i < year.length; i ++){
  for (var j = 0; j < itemName.length; j++){
    temp_arr[j] = lineMap[year[i]][itemName[j]];
  }
  console.log("Index: " + i  + "\n" + temp_arr);
  arr[i] = temp_arr;
}
console.log(arr);

At the 5th line (console.log(temp_arr)), the console printed out what I expected--an array of the item count of its respective iteration:

'Index: 0
33,33,47,13,33'
'Index: 1
8,47,8,12,33'
'Index: 2
8,33,47,12,13'
'Index: 3
8,8,47,8,47'

However, at the 8th line (console.log(arr)), I am not receiving my expected output. Instead, I am getting:

var arr = [
  [8, 8, 47, 8, 47],
  [8, 8, 47, 8, 47],
  [8, 8, 47, 8, 47],
  [8, 8, 47, 8, 47]
]

Upvotes: 2

Views: 234

Answers (5)

kimchi_is_life
kimchi_is_life

Reputation: 27

I created a function called yearsItemCount(); Just call this and it will return what you want... Although it's O(N^2)

yearsItemCount = () => {

    let finalArr = []

      for(var line in lineMap) {
          let arr = [];
        for(var i in lineMap[line]) {
          arr.push(lineMap[line][i])
        }
        finalArr.push(arr);
      }
      return finalArr;
    }

or

Object.values(lineMap).map((a, b) => {
  return Object.values(a);
})

I don't know what's faster.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074188

First, a warning: The order of the properties in those objects is defined, now, as of ES2015, but relying on that order is generally a bad idea. Also, you seem to be using only ES5 level features, which don't define the order.

So the best way, without relying on order, is to give yourself an array of the property names in the order you want them in your result arrays:

var keys = [
    "Hemoglobin",
    "Lysozyme",
    "Myoglobin",
    "DNA",
    "Cytochrome C"
];

Then something like this (see comments):

// Loop through the years sorted lexicographically, building the result array
var arr = Object.keys(lineMap).sort().map(function(mainKey) {
    // Loop through the property names in the defined order, building
    // each inner array
    var entry = lineMap[mainKey];
    return keys.map(function(key) {
        return entry[key];
    });
});

Live Example:

var keys = [
    "Hemoglobin",
    "Lysozyme",
    "Myoglobin",
    "DNA",
    "Cytochrome C"
];
var lineMap = {
  2016: {
    Hemoglobin: 33 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 13 ,
    "Cytochrome C": 33
  },
  2017: {
    Hemoglobin: 8 ,
    Lysozyme: 47 ,
    Myoglobin: 8 ,
    DNA: 12 ,
    "Cytochrome C": 33
  },
  2018: {
    Hemoglobin: 8 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 12 ,
    "Cytochrome C": 13
  },
  2019: {
    Hemoglobin: 8 ,
    Lysozyme: 8 ,
    Myoglobin: 47 ,
    DNA: 8 ,
    "Cytochrome C": 47
  }
};

// Loop through the years sorted lexicographically, building the
// result array
var arr = Object.keys(lineMap).sort().map(function(mainKey) {
    // Loop through the property names in the defined order, building
    // each inner array
    var entry = lineMap[mainKey];
    return keys.map(function(key) {
        return entry[key];
    });
});

console.log(arr);

If you want the years in numeric order, just add a callback to the sort:

.sort(function(a, b) { return a - b; })

Tackling it in an ES2015+ world, I'd still keep the separate list of keys in the order you want them, so not a lot changes, it just gets a bit more concise:

const arr = Object.entries(lineMap)
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([_, entry]) => keys.map(key => entry[key]));

Live Example:

const keys = [
    "Hemoglobin",
    "Lysozyme",
    "Myoglobin",
    "DNA",
    "Cytochrome C"
];
const lineMap = {
  2016: {
    Hemoglobin: 33 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 13 ,
    "Cytochrome C": 33
  },
  2017: {
    Hemoglobin: 8 ,
    Lysozyme: 47 ,
    Myoglobin: 8 ,
    DNA: 12 ,
    "Cytochrome C": 33
  },
  2018: {
    Hemoglobin: 8 ,
    Lysozyme: 33 ,
    Myoglobin: 47 ,
    DNA: 12 ,
    "Cytochrome C": 13
  },
  2019: {
    Hemoglobin: 8 ,
    Lysozyme: 8 ,
    Myoglobin: 47 ,
    DNA: 8 ,
    "Cytochrome C": 47
  }
};

const arr = Object.entries(lineMap)
    .sort(([a], [b]) => a.localeCompare(b))
    .map(([_, entry]) => keys.map(key => entry[key]));

console.log(arr);

That takes advantage of Object.entries, arrow functions, and destructuring assignment.

And again, if you want those years in numeric order:

const arr = Object.entries(lineMap)
    .sort(([a], [b]) => a - b)
    .map(([_, entry]) => keys.map(key => entry[key]));

Upvotes: 1

Marco Bonelli
Marco Bonelli

Reputation: 69286

First of all, you are using the exact same array every time, so when you do arr[i] = temp_arr you are assigning the same copy to each position of arr. You should create a new array each time. Secondly, you cannot know the order of an object's keys, there is no guarantee that the keys will be ordered when iterating over them. With this said, you could still use a fixed list of keys or sort them first.

let orderedKeys = ['Hemoglobin', 'Lysozyme', 'Myoglobin', 'DNA', 'Cytochrome C'];
let arr = Object.values(lineMap).map(el => orderedKeys.map(k => el[k]))

Result:

> arr
0: [33, 33, 47, 13, 33]
1: [8, 47, 8, 12, 33]
2: [8, 33, 47, 12, 13]
3: [8, 8, 47, 8, 47]

Upvotes: 0

Alex D
Alex D

Reputation: 980

If you're ok with ES6 syntax this will do the trick:

let result = [];
Object.values(lineMap).map(obj =>result.push(Object.values(obj)))

This will get you an array of arrays like you wanted. If you prefer a single array with all the results you can spread the second Object.values(obj) like so:

Object.values(lineMap).map(obj =>result.push(...Object.values(obj)))

Upvotes: 0

epascarello
epascarello

Reputation: 207501

Your issue looks like the problem is with arr[i] = temp_arr; and sharing the same array reference. You need to start with a new array on each iteration. Or you need to clone it when copying it. arr[i] = temp_arr.slice(0)

Object.entries and map() will make your job easier.

var lineMap = {
  2016: {
    Hemoglobin: 33,
    Lysozyme: 33,
    Myoglobin: 47,
    DNA: 13,
    'Cytochrome C': 33
  },
  2017: {
    Hemoglobin: 8,
    Lysozyme: 47,
    Myoglobin: 8,
    DNA: 12,
    'Cytochrome C': 33
  },
  2018: {
    Hemoglobin: 8,
    Lysozyme: 33,
    Myoglobin: 47,
    DNA: 12,
    'Cytochrome C': 13
  },
  2019: {
    Hemoglobin: 8,
    Lysozyme: 8,
    Myoglobin: 47,
    DNA: 8,
    'Cytochrome C': 47
  }
}

console.log(Object.values(lineMap).map(o=>Object.values(o)))

Upvotes: 0

Related Questions