1

I have an array of objects I need to transform into new objects. some value as keys and an array of values.

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
]

and the desired result is

const result = {
  user: [1, 2, 3, 4],
  group: [1, 2]
}

what I've tried using reduced but it not what I'm expected.

const result = data.reduce((acc, { type, ...obj }) => {
  acc[type] = data.map(item => item.id)
  return acc;
}, {})

result = { user: [ 1, 2, 1, 2, 3, 4 ], group: [ 1, 2, 1, 2, 3, 4 ] }
Teerapat
  • 112
  • 2
  • 7

6 Answers6

2

Unless you're doing functional programming with predefined reducer functions, reduce is usually an overcomplicated tool. Just use a loop.

In this case, you'd start with an object with user and group properties that have empty arrays, then add to the right array using a dynamic property name:

const result = { user: [], group: [] };
for (const {type, id} of data) {
    result[type].push(id);
}

Live Example:

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
];

const result = { user: [], group: [] };
for (const {type, id} of data) {
    result[type].push(id);
}
console.log(result);

If the type values aren't known in advance, add the arrays dynamically:

const result = { user: [], group: [] };
for (const {type, id} of data) {
    if (!result[type]) {
        result[type] = [];
    }
    result[type].push(id);
}

Live Example:

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
];

const result = { user: [], group: [] };
for (const {type, id} of data) {
    if (!result[type]) {
        result[type] = [];
    }
    result[type].push(id);
}
console.log(result);

If you like being really terse, you could use the new ??= operator to add the array when it's not there yet (for me, in this case, it's going too far and losing clarity):

const result = { user: [], group: [] };
for (const {type, id} of data) {
    (result[type] ??= []).push(id);
}

Live Example:

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
];

const result = { user: [], group: [] };
for (const {type, id} of data) {
    (result[type] ??= []).push(id);
}
console.log(result);

You can shoehorn this into a reduce (because you can shoehorn any array operation into a reduce), but doing so doesn't buy you anything. Here's that first one using reduce instead of a loop:

const result = data.reduce((acc, {type, id}) => {
    acc[type].push(id);
    return acc;
}, {user: [], group: []});

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
];

const result = data.reduce((acc, {type, id}) => {
    acc[type].push(id);
    return acc;
}, {user: [], group: []});
console.log(result);
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

Each iterate, you check if the accumulate object has the type, if yes, push the iterated element's id to the existing array, else init the array

const data = [
  {
    type: "user",
    id: 1,
  },
  {
    type: "user",
    id: 2,
  },
  {
    type: "group",
    id: 1,
  },
  {
    type: "group",
    id: 2,
  },
  {
    type: "user",
    id: 3,
  },
  {
    type: "user",
    id: 4,
  },
]

const res = data.reduce((acc, el) => {
  if (acc[el.type]) {
    acc[el.type].push(el.id)
  } else {
    acc[el.type] = [el.id]
  }

  return acc
}, {})

console.log(res)
hgb123
  • 13,869
  • 3
  • 20
  • 38
1

You need to check if the property does not exists and push id to it.

const
    data = [{ type: 'user', id: 1 }, { type: 'user', id: 2 }, { type: 'group', id: 1 }, { type: 'group', id: 2 }, { type: 'user', id: 3 }, { type: 'user', id: 4 }],
    result = data.reduce((acc, { type, id }) => {
        acc[type] ??= [];
        acc[type].push(id);
        return acc;
    }, {});

console.log(result);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0

If the property doesn't exist on the accumulator add it, and if it does push the id into it:

const data = [{"type":"user","id":1},{"type":"user","id":2},{"type":"group","id":1},{"type":"group","id":2},{"type":"user","id":3},{"type":"user","id":4}];

const result = data.reduce((acc, { type, id }) => {
  if(!acc[type]) acc[type] = [];
  
  acc[type].push(id);

  return acc;
}, {})

console.log(result)
Ori Drori
  • 183,571
  • 29
  • 224
  • 209
0

How about this?

A vanilla version of groupBy()

const groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x.id);
    return rv;
  }, {});
};

You could easily call group by a key using,

const result = groupBy(data, 'type');

Full code:

const groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x.id);
    return rv;
  }, {});
};

const data = [
  {
    type: 'user',
    id: 1
  },
  {
    type: 'user',
    id: 2
  },
  {
    type: 'group',
    id: 1
  },
  {
    type: 'group',
    id: 2
  },
  {
    type: 'user',
    id: 3
  },
  {
    type: 'user',
    id: 4
  }
]

const result = groupBy(data, 'type');

console.log(result); // { group: [1, 2], user: [1, 2, 3, 4] }

Reference link.

IRSHAD
  • 1,582
  • 11
  • 16
-1

Here is a solution using Object.fromEntries to build the skeleton for the result object (with empty arrays). Then a simple loop can populate those arrays, and you're done:

let data = [{ type: 'user', id: 1 }, { type: 'user', id: 2 }, { type: 'group', id: 1 }, { type: 'group', id: 2 }, { type: 'user', id: 3 }, { type: 'user', id: 4 }];
let result = Object.fromEntries(data.map(({type}) => [type, []]));
for (let {type, id} of data) result[type].push(id);

console.log(result);
trincot
  • 317,000
  • 35
  • 244
  • 286