2

How do I reduce this object array and and map it to a new array?

My data:

var objArray = 
  [{
    state: 'NY',
    type: 'A',
    population: 100
  },
  {
    state: 'NY',
    type: 'A',
    population: 300
  },
  {
    state: 'NY',
    type: 'B',
    population: 200
  },
  {
    state: 'CA',
    type: 'A',
    population: 400
  },
  {
    state: 'CA',
    type: 'A',
    population: 400
  }];

If an entry has the same state AND type I need to combine it into a single entry and sum their populations.

Finally I need to map it to an array in this format.

 var outputArray = [ ['A', 'NY', 400 ], ['B', 'NY', 200], ['A', 'CA', 800] ]
2
  • use Array#reduce and Array#map Commented Oct 11, 2017 at 22:39
  • 3
    You need to show what you have tried. Stackoverflow isn't a free code writing or tutorial service. The objective here is to help you fix your code Commented Oct 11, 2017 at 22:41

5 Answers 5

6

Well, first you'd want to reduce it. This can be done like so...

objArray.reduce((prev, obj) => {
    if(1 + (indx = prev.findIndex(oldObj => oldObj.type === obj.type && oldObj.state === obj.state))) {
        prev[indx].population += obj.population;
    } else {
        prev.push({...obj})
    }
    return prev;
}, [])

This takes the array being gathered, and modifies it in some way and returns it in the reduce callback. It will either modify an existing value's population, if it can find one with the correct state and type; or it will append a new object to the end of the array.

Now you need to map it.

.map(obj => [ obj.type, obj.state, obj.population ])
Sign up to request clarification or add additional context in comments.

10 Comments

note, that this code mutates the original array too
It mutates prev, but that's different each iteration and is intentionally mutated. I don't think objArray is, though -- right?
console.log(objArray) - you'll see that it has changed population in items 0 and 3
you push obj - objects are pass by reference - so, when you update prev[0].population, it's the same object as objArray[0], so objArray[0].population is the same property as prev[0].population
@jm22 When you put a key without a value in a JS object literal, it's a shorthand for obj: obj, i.e. pushing itself. You have to use the spread op to push its contents, not itself
|
1

I would recommend using the lodash package (it is an extremely common package in Javascript applications). It adds a lot of great functions for manipulating arrays. This post explains how to sum values on groups. You will need to modify this answer slightly to account for your two parameters, which you can do by changing the groupBy command to this:

_.groupBy(objArray, function(val){
    return val.state + "#" + val.type
})

Comments

1

You could try something like this:

var arr = Object.keys(objArray).map(function (key) { return Object.keys(objArray[key]).map(function (key) { return [[objArray[0].state, objArray[0].type, objArray[0].population],[objArray[1].state, objArray[1].type, objArray[1].population]]}); })[0][0];

Comments

1

If you know state and type will never have a certain character such as '_' you can make a key out of state and type such as 'NY_A' - a little like a composite key in a DB. Then you just create an object with these keys, add the populations and then pull them apart into an array:

Object.entries(
    objArray.reduce((acc,curr) => (
    acc[curr.type + '_' + curr.state] = curr.population + (acc[curr.type + '_' + curr.state] || 0)
    , acc), {}))
.map(item => [...item[0].split('_'), item[1]])

Comments

1
const _ = require('lodash')

const input = [
  { state: 'NY', type: 'A', population: 100 },
  { state: 'NY', type: 'A', population: 300 },
  { state: 'NY', type: 'B', population: 200 },
  { state: 'CA', type: 'A', population: 400 },
  { state: 'CA', type: 'A', population: 400 }
]

const expected = [
  ['A', 'NY', 400],
  ['B', 'NY', 200],
  ['A', 'CA', 800]
]

const output = _(input)
  .groupBy(({ state, type }) => [state, type].join(':'))
  .mapValues((states) => states.reduce((total, { population }) => total + population, 0))
  .map((population, stateType) => {
    const [state, type] = stateType.split(':')
    return [type, state, population]
  })
  .value()

console.log(expected)
console.log(output)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.