Skip to content

Instantly share code, notes, and snippets.

@brian-mcallister-lab49
Last active June 22, 2020 15:40
Show Gist options
  • Save brian-mcallister-lab49/2027e93bc2140a72e8dbff2f7dcd7c1e to your computer and use it in GitHub Desktop.
Save brian-mcallister-lab49/2027e93bc2140a72e8dbff2f7dcd7c1e to your computer and use it in GitHub Desktop.
TIL-Lab49/Creating a unique list of JavaScript objects, when the objects are not unique.

There are lots of code snippets on the internet for creating unique lists of things. Most work great, and this technique is very common and easy to understand:

const list = ['a', 'a', 'a', 'b', 'b', 'c', 'c'];
const unique = [...new Set(list)];
// #=> ['a', 'b', 'c'];

But what if you have a list of complex objects? It's not entirely uncommon in real applications to have multiple lists of objects where those objects aren't shared references. If you want to combine those lists to get a unique list of objects, it's going to take a little more work.

const listA = [
  { type: 'type-one', value: 1 },
  { type: 'type-two', value: 2 },
];
const listB = [
  { type: 'type-one', value: 1 },
  { type: 'type-two', value: 2 },
  { type: 'type-three', value: 3 },
];
const listC = [
  { type: 'type-three', value: 3 },
  { type: 'type-four', value: 4 },
];

With this sample data, you could do something similar to the above technique, maybe something like:

const list = [...listA, ...listB, ...listC];
const unique = [...new Set(list.map(item => item.type))];

...but the result would be ["type-one", "type-two", "type-three", "type-four"]. Instead, we need to map the values back to objects from the original list. So the next step would be:

const list = [...listA, ...listB, ...listC];
const keys = [...new Set(list.map(item => item.type))];
const unique = keys.map(key => list.find(item => item.type === key));

Which would result in a flat array of unique items. Finally, you could wrap the whole thing up in a function:

const uniqueByKey = <T extends object, U extends keyof T>(key: U, ...items: T[][]) => {
  const flat = items.reduce((acc, next) => ([...acc, ...next]), []);
  const keys = [...new Set(flat.map(item => item[key]))];
    
  return keys.map(flatKey => flat.find(item => item[key] === flatKey));
}

Here's a TypeScript playground to see this in action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment