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