i have an array of object, and one element of the object is an array
example array to sort:
[
{id: 1, values: ['a','b']},
{id: 2, values: ['c','d']},
{id: 3, values: ['b']},
{id: 4, values: ['c']},
{id: 5, values: ['e']},
]
i need to make a function to pass to the sort function that given a value string, start the sorting of item with that value and continue with other items that have the same value:
sorted example array given 'b':
by id: 1,3,2,4,5
so first it will put all the items with a 'b' value, then the next element, could be anything, ad after that the other items that have the same value, (id 2,4 have c in common) and so on
i hope that i've explained it good enough, thanks
CodePudding user response:
Steps:
- Loop the data and take the
id
s of any object that contains the target letter. - Filter out those taken data
- Sort the filtered data by the first letter in the
values
array alphabetically. - Loop the sorted data and take the
id
s into the result array.
Change the data
to see any test cases you want.
const data = [
{id: 1, values: ['a','b']},
{id: 2, values: ['c','d']},
{id: 3, values: ['b']},
{id: 4, values: ['c']},
{id: 5, values: ['e']},
{id: 6, values: ['a']},
{id: 7, values: ['b']},
]
function sortByLetter(data, letter){
let dataClone = data.map(a => ({...a}));
let result = [];
dataClone.forEach(d => {
if (d.values.includes(letter)) {
result.push(d.id);
}
})
let filteredData = dataClone.filter(d => !result.includes(d.id));
filteredData.sort((a, b) => a.values[0].localeCompare(b.values[0]))
filteredData.forEach(fd => result.push(fd.id))
return result;
}
console.log(sortByLetter(data,'b'));
CodePudding user response:
I would not do this with a callback function for sort
, since the order of two objects does not depend only on these objects themselves, but on all objects. This would mean that for every call of the callback the whole input has to be processed, eliminating the higher priority objects, to then find out which of the two given objects would come first. This would give a rather inefficient algorithm.
Instead, define a function that does the sorting.
It would repeatedly find the value that occurs most often, and then output the objects that have that value. Care has to be taken to remove those objects as candidate once they are output.
Here is an implementation:
function sortByFrequency(data, selectedValue) {
// Key the data by id
const remaining = Object.fromEntries(data.map(({id, values}, idx) => [id, {values, idx}]));
// Create inverse data structure (for a given value, give the associated ids)
const idsWithValue = {};
for (const {id, values} of data) {
for (const value of values) {
(idsWithValue[value] ??= new Set).add(id);
}
}
const results = [];
while (results.length < data.length) {
if (selectedValue) {
// Output the (remaining) objects that have the selected value:
for (const id of idsWithValue[selectedValue]) {
results.push(data[remaining[id].idx]);
for (const value of remaining[id].values) {
idsWithValue[value].delete(id); // Remove this id as association for a value
}
}
}
let maxLength = 0;
// Find the value with the most associated ids:
for (const [value, ids] of Object.entries(idsWithValue)) {
if (ids.size > maxLength) {
maxLength = ids.size;
selectedValue = value;
}
}
}
return results;
}
// Example run
const data = [
{id: 1, values: ['a','b']},
{id: 2, values: ['c','d']},
{id: 3, values: ['b']},
{id: 4, values: ['c']},
{id: 5, values: ['e']},
{id: 6, values: ['a']},
{id: 7, values: ['b']},
];
const results = sortByFrequency(data, "b");
console.log(results);
The efficiency could be improved by making use of an efficient priority queue implementation. But as JavaScript offers no native priority queue, you'd have to include a library or create your own. So I left that consideration aside.