0

How can I create objects from a Set?

const dupObj = [{ id: 1, value: 'a' }, { id: 2, value: 'b' }, { id: 1, value: 'c' }];
const uniKeys = [...(new Set(dupObj.map(({ id }) => id)))];
// [ '1', '2' ]

I am trying to get this without looping uniKeys:

[ {val: '1'}, {val: '2'} ]

Is it possible?

3
  • 3
    You're looping 3 times already in the creation of uniKeys... Commented Oct 27 at 1:04
  • 2
    I would use .reduce() to build the result, skipping id values that have already been seen. Then the array is just the .values of the result object. Commented Oct 27 at 1:04
  • 1
    If [...set] or Array.from(set) counts as an iteration for set, then you can skip one iteration using Array.from with a map function, see jsFiddle. Commented Oct 27 at 2:20

4 Answers 4

3

Iterate the input keeping track of ids in a set, but don't iterate the set. Push previously unseen ids to a result array. Just one loop.

const dupObj = [ { id: 1, value: 'a' }, { id: 2, value: 'b' }, { id: 1, value: 'c' } ];

const result = [];
const ids = new Set();

for (const { id } of dupObj) {
  if (!ids.has(id)) {
    ids.add(id);
    result.push({ val: id });
  }
}

console.log(result);

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

Comments

2

You currently have 3 iterations already in the creation of uniKeys, and would need another iteration to get to your desired output format, resulting in a total of 4 iterations.

Since you need to use some kind of intermediate data structure to eliminate duplicates(1), you can at best bring this down to 2 iterations(2):

  1. Iteration 1: loop over the input data and build the intermediate data structure (could be a Set, a Map or an object) with the intent of eliminating duplicates.
  2. Iteration 2: extract data from the intermediate data structure to build the output array.

There are many possible variations, but here is one that uses a plain JavaScript object with a reduce() operation for the first iteration, and Object.values() for the second iteration:

const data = [{ id: 1, value: 'a' }, { id: 2, value: 'b' }, { id: 1, value: 'c' }];

const result = Object.values(data.reduce((a, { id }) => {
  a[id] ??= { val: `${id}` };
  return a;
}, {}));

console.log(result);


(1) There is an approach where you don't build an intermediate data structure and look for potential duplicates in the output array itself (using find() or some()), but this would result in a time complexity of O(n^2) instead of the O(2n) you would get otherwise.
(2) I stand corrected on this. It's trivially doable in one iteration as shown in this answer with a small trade-off in space complexity.

Comments

1

As I know you can't do it purely from the Set -> because Set only stores values, not structures.

const dupObj = [
  { id: 1, value: 'a' },
  { id: 2, value: 'b' },
  { id: 1, value: 'c' }
];

const result = [...new Set(dupObj.map(({ id }) => id))].map(val => ({ val }));
console.log(result); // [ { val: 1 }, { val: 2 } ]

1 Comment

Please note that there are 4 loops in this idea: (1) map input ids, (2) add them to a set, (3) spread the set into an array, (4) map that array to resahape into { val } objects. If the OP is not worried about loops or intermediate arrays, but instead looking for more terse, functional code, we can save some ink here by skipping the destructuring assignment in the inner-most loop: ...new Set(dupObj.map(o=>o.id)
0

As there are multiple possible solutions, one can also consider my old suggestion from comments: to use Array.from(set, mapper) to convert the set to the final array in one step, instead of using the spread operator [...set]. This will keep the number of iterations to three (first seen by Robby Cornelissen), as the ecmascript specification for Array.from sets the application of mapper inside the loop that builds the array:

const dupObj = [{ id: 1, value: 'a' }, { id: 2, value: 'b' }, { id: 1, value: 'c' }];
const uniObj = Array.from(new Set(dupObj.map(({ id }) => id)), id => ({val: id}));
console.log(uniObj);

or as a jsFiddle

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.