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 ] }

6 Answers 6

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)

Sign up to request clarification or add additional context in comments.

Comments

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);

Comments

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);

2 Comments

UV. When did the ??= came into being. I see that there is also ||= - is it old or new?
@OriDrori - They were added in ES2021, here's the (finished) proposal.
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)

Comments

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.

Comments

-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);

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.