T Porter
T Porter

Reputation: 1281

Better way to map an array of objects to two arrays, one of unique keys and another of number of occurences

I have an array of objects, and I'm struggling to come up with a better way to map it to two new arrays: one containing a list of unique code values, and the other containing a count of occurrences of each code.

Note: The order of the resulting arrays are important. For instance, data[1] should be the number of occurrences of labels[1].

This is what I have so far, but I can't seem to find a way to do it without multiple loops...

var objs = [{
    code: 200,
    message: 'a'
  },
  {
    code: 300,
    message: 'b'
  },
  {
    code: 200,
    message: 'c'
  },
  {
    code: 400,
    message: 'd'
  },
  {
    code: 200,
    message: 'e'
  },
];

var data = [];
var labels = [];

var bins = objs
  .reduce((codes, obj) => {
    let key = obj.code;
    codes[key] ? codes[key]++ : codes[key] = 1;
    return codes;
  }, {});

for (var prop in bins) {
  if (!bins.hasOwnProperty(prop))
    continue;

  labels.push(prop);
  data.push(bins[prop]);
}

console.log('data', data);      // [3, 1, 1]
console.log('labels', labels);  // ['200', '300', '400']

Upvotes: 1

Views: 237

Answers (4)

Ori Drori
Ori Drori

Reputation: 193248

You can use reduce the array of objects into a Map, and then extract the values and the keys into arrays:

const values = [{ code: 200, message: 'a' }, { code: 300, message: 'b' }, { code: 200, message: 'c' }, { code: 400, message: 'd' }, { code: 200, message: 'e' }];

const counts = values.reduce((m, { code }) => m.set(code, (m.get(code) || 0) + 1), new Map());

const data = [...counts.values()];
const label = [...counts.keys()];

console.log(data);
console.log(label);

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386868

You could take a hash table for the indices for updating the arrays.

var values = [{ code: 200, message: 'a' }, { code: 300, message: 'b' }, { code: 200, message: 'c' }, { code: 400, message: 'd' }, { code: 200, message: 'e' }],
    hash = Object.create(null),
    data = [],
    label = [];

values.forEach(function (o) {
    if (o.code in hash) {
        data[hash[o.code]] += 1;
        return;
    }
    hash[o.code] = data.push(1) - 1;
    label.push(o.code);
});

console.log(data);
console.log(label);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Upvotes: 1

Yoni Lerner
Yoni Lerner

Reputation: 52

This look like you want some object which is like an array and can have key-value. I recommend using Map

have a look here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

with this you could make this with one loop and receive the correct result you needed

Upvotes: 1

Kalob Porter
Kalob Porter

Reputation: 440

A little more clean using a newer version of Javascript, should work the same though. Check it out.

const objs = [{
    code: 200,
    message: 'a'
  },
  {
    code: 300,
    message: 'b'
  },
  {
    code: 200,
    message: 'c'
  },
  {
    code: 400,
    message: 'd'
  },
  {
    code: 200,
    message: 'e'
  },
];

const data = [];
const labels = [];

objs.forEach(obj => {
  if (labels.indexOf(obj.code) < 0) {
  	labels.push(obj.code);
    data.push(1);
  } else {
  	const index = labels.indexOf(obj.code);
    data[index] ++;
  }
});

console.log('data', data);
console.log('labels', labels);

Upvotes: 1

Related Questions