I have two arrays and I want to sort first one based on some values from another array:
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
I want to sort my first array, by property 'priority' from my second array -> from the biggest one.
Like this:
const expectedResult = [
['quizes', ['arr']],
['music', ['arr']]
['movies', ['arr']],
['series', ['arr']],
];
This is what I tried but without success.
const sorted = items.sort((a,b) => {
const [aKey, aVal] = a;
const [bKey, bVal] = b;
const prio = categories.filter(c => c.name === aKey)[0];
// not sure how to use this prio
return aKey.priority - bKey.priority;
})
CodePudding user response:
You were very close, you just needed to grab b's priority (and also use the priority property). find rather than filter is a good choice:
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = categories.find(cat => cat.name === aKey).priority;
const bPriority = categories.find(cat => cat.name === bKey).priority;
return bPriority - aPriority;
});
Live Example
const items = [
["music", ["arr"]],
["movies", ["arr"]],
["quizes", ["arr"]],
["series", ["arr"]]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = categories.find(cat => cat.name === aKey).priority;
const bPriority = categories.find(cat => cat.name === bKey).priority;
return bPriority - aPriority;
});
console.log(sorted);
.as-console-wrapper {
max-height: 100% !important;
}
But repeatedly traversing that array of categories isn't a good idea if items is long. Instead, make a Map of key to priority, then use that:
const catPriorityMap = new Map(categories.map(({name, priority}) => [name, priority]));
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = catPriorityMap.get(aKey);
const bPriority = catPriorityMap.get(bKey);
return bPriority - aPriority;
});
Live Example
const items = [
["music", ["arr"]],
["movies", ["arr"]],
["quizes", ["arr"]],
["series", ["arr"]]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const catPriorityMap = new Map(categories.map(({name, priority}) => [name, priority]));
const sorted = items.sort((a,b) => {
const [aKey] = a;
const [bKey] = b;
const aPriority = catPriorityMap.get(aKey);
const bPriority = catPriorityMap.get(bKey);
return bPriority - aPriority;
});
console.log(sorted);
.as-console-wrapper {
max-height: 100% !important;
}
Lookup in a Map is done in sublinear time, whereas finding somethign in an array is done in linear time.
CodePudding user response:
You can use sort() method and check the priority from the categories
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']],
];
const categories = [
{ name: 'music', priority: 3 },
{ name: 'movies', priority: 2 },
{ name: 'quizes', priority: 5 },
{ name: 'series', priority: 1 },
{ name: 'sports', priority: 4 },
];
const result = items.sort(([a], [b]) => {
const aPriority = categories.find(({ name }) => name === a).priority;
const bPriority = categories.find(({ name }) => name === b).priority;
return bPriority - aPriority;
});
console.log(result);
CodePudding user response:
You really do not want to use find() or filter() inside of the sort method because it is expensive. On every iteration, you are looking up the data in the array. So you are looping a lot. There is better ways to get the index.
Easiest thing is to make a lookup object so you are not having to search the other array over and over for a match. So if you can change categories to an object from the start, it will make your life so much easier.
In the sort I added max value in case the key is not defined. Now this would backfire if you had a value of zero since it is just a truthy check.
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = {
music: 3,
movies: 2,
quizes: 5,
series: 1,
sports: 4,
};
items.sort(([keyA], [keyB]) => (categories[keyA] || Number.MAX_VALUE) - (categories[keyB] || Number.MAX_VALUE));
console.log(items);
If you can not make the object look like that and you have to use the array, you can convert it from the array to an object. That can be done a few ways. I like to use reduce.
const items = [
['music', ['arr']],
['movies', ['arr']],
['quizes', ['arr']],
['series', ['arr']]
];
const categories = [
{ name: "music", priority: 3},
{ name: "movies", priority: 2},
{ name: "quizes", priority: 5},
{ name: "series", priority: 1},
{ name: "sports", priority: 4},
];
const lookup = categories.reduce((acc, obj) => ({...acc, [obj.name]: obj.priority}), {});
items.sort(([keyA], [keyB]) => (lookup[keyA] || Number.MAX_VALUE) - (lookup[keyB] || Number.MAX_VALUE));
console.log(items);
Now if you are sure that all of the keys will exist in the categories, you can drop the max value
items.sort(([keyA], [keyB]) => lookup[keyA] - lookup[keyB];
CodePudding user response:
You could take an object for the order and sort by delta.
const
items = [['music', ['arr']], ['movies', ['arr']], ['quizes', ['arr']], ['series', ['arr']]],
categories = [{ name: "music", priority: 3 }, { name: "movies", priority: 2 }, { name: "quizes", priority: 5 }, { name: "series", priority: 1 }, { name: "sports", priority: 4 }],
order = Object.fromEntries(
categories.map(({ name, priority }) => [name, priority])
);
items.sort(([a], [b]) => order[a] - order[b]);
console.log(items);
.as-console-wrapper { max-height: 100% !important; top: 0; }
