So I have this array of objects which consists of items where one item have 6 combinations which are posed as objects.
Suppose I have one combination of this object:
{ "1": "ab", "2": "bc", "3": "ac" }
The array also consists of 5 other objects with different combinations of that object. Like this:
{ "1": "ab", "2": "ac", "3": "bc" }
{ "1": "ac", "2": "bc", "3": "ab" }
{ "1": "ac", "2": "ab", "3": "bc" }
{ "1": "bc", "2": "ac", "3": "ab" }
{ "1": "bc", "2": "ab", "3": "ac" }
This way I have around 1000 items with 6 combinations of each of them in an array and I need to filter and return only 1 combination of each item.
So suppose this is an array of 12 different combinations of two items:
[
{ "1": "ab", "2": "bc", "3": "ac" }
{ "1": "ab", "2": "ac", "3": "bc" }
{ "1": "ac", "2": "bc", "3": "ab" }
{ "1": "ac", "2": "ab", "3": "bc" }
{ "1": "bc", "2": "ac", "3": "ab" }
{ "1": "bc", "2": "ab", "3": "ac" }
{ "1": "de", "2": "ef", "3": "df" }
{ "1": "df", "2": "ef", "3": "de" }
{ "1": "de", "2": "df", "3": "ef" }
{ "1": "ef", "2": "df", "3": "de" }
{ "1": "df", "2": "de", "3": "ef" }
{ "1": "ef", "2": "de", "3": "df" }
]
I want to filter that array and return only 1 combination of each, like so:
[
{ "1": "ab", "2": "bc", "3": "ac" }
{ "1": "de", "2": "ef", "3": "df" }
]
Please note that "ab" in first object can be a value in whole different combination like:
{ "1": "ab", "2": "df", "3": "fe" }
How can I do this efficiently and with good performance in mind?
CodePudding user response:
If the values are always strings you can create a composite key from the sorted Object.values()
of each element and then use a simple 'group by' operation, here using reduce()
accumulating into a Map.
I'm only setting on the first match here (only if the map doesn't have a matching entry) which will return the first match, if you want the last match, just set the key on every iteration.
const input = [
{ 1: 'ab', 2: 'bc', 3: 'ac' },
{ 1: 'ab', 2: 'ac', 3: 'bc' },
{ 1: 'ac', 2: 'bc', 3: 'ab' },
{ 1: 'ac', 2: 'ab', 3: 'bc' },
{ 1: 'bc', 2: 'ac', 3: 'ab' },
{ 1: 'bc', 2: 'ab', 3: 'ac' },
{ 1: 'de', 2: 'ef', 3: 'df' },
{ 1: 'df', 2: 'ef', 3: 'de' },
{ 1: 'de', 2: 'df', 3: 'ef' },
{ 1: 'ef', 2: 'df', 3: 'de' },
{ 1: 'df', 2: 'de', 3: 'ef' },
{ 1: 'ef', 2: 'de', 3: 'df' },
];
const result = [
...input
.reduce((a, o) => {
const key = Object.values(o)
.sort((a, b) => a.localeCompare(b))
.join('_');
if (!a.has(key)) a.set(key, o);
return a;
}, new Map())
.values(),
];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
If you don't mind returning the last matched combination you can simplify this into a map()
within a new Map()
call.
const input = [{ 1: 'ab', 2: 'bc', 3: 'ac' }, { 1: 'ab', 2: 'ac', 3: 'bc' }, { 1: 'ac', 2: 'bc', 3: 'ab' }, { 1: 'ac', 2: 'ab', 3: 'bc' }, { 1: 'bc', 2: 'ac', 3: 'ab' }, { 1: 'bc', 2: 'ab', 3: 'ac' }, { 1: 'de', 2: 'ef', 3: 'df' }, { 1: 'df', 2: 'ef', 3: 'de' }, { 1: 'de', 2: 'df', 3: 'ef' }, { 1: 'ef', 2: 'df', 3: 'de' }, { 1: 'df', 2: 'de', 3: 'ef' }, { 1: 'ef', 2: 'de', 3: 'df' },];
const result = [
...new Map(
input.map((o) => [
Object.values(o)
.sort((a, b) => a.localeCompare(b))
.join('_'),
o,
])
).values(),
];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
Here's a quick example of how you might sort the result array returned by the filter. It's just a function that accepts a compare function, sorts the Object.values
of the passed combination object and then rebuilds the object from the sorted array of values (here using Object.fromEntries()
, but if you don't mind 0 indexed objects you could use Object.assign()
and forego the extra map()
, ie. return Object.assign({}, Object.values(obj).sort(compare));
)
const input = [
{ 1: 'ab', 2: 'bc', 3: 'ac' },
{ 1: 'ab', 2: 'ac', 3: 'bc' },
{ 1: 'ac', 2: 'bc', 3: 'ab' },
{ 1: 'ac', 2: 'ab', 3: 'bc' },
{ 1: 'bc', 2: 'ac', 3: 'ab' },
{ 1: 'bc', 2: 'ab', 3: 'ac' },
{ 1: 'de', 2: 'ef', 3: 'dc' },
{ 1: 'dc', 2: 'ef', 3: 'de' },
{ 1: 'de', 2: 'dc', 3: 'ef' },
{ 1: 'ef', 2: 'dc', 3: 'de' },
{ 1: 'dc', 2: 'de', 3: 'ef' },
{ 1: 'ef', 2: 'de', 3: 'dc' },
];
const result = [...new Map(input.map((o) => [Object.values(o).sort((a, b) => a.localeCompare(b)).join('_'), o,])).values(),];
console.log('Initial result: ', result);
function sort_combination(obj, compare = (a, b) => a.localeCompare(b)) {
return Object.fromEntries(
Object.values(obj)
.sort(compare)
.map((v, i) => [i 1, v])
);
}
const includes_c = (a, b) => b.includes('c') - a.includes('c') || a.localeCompare(b);
const sorted = result.map((o) => sort_combination(o, includes_c));
console.log('Sorted result: ', sorted);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>