How sort array with objects by two diffrent keys? - javascript

I have no idea how I can sort an array of objects comparing two keys. I have array:
const arr = [
{
age: "20",
group: "XXX",
id: "3L1aa1558002753379",
menu: "standard",
name: "Adam"
},
{
age: "22",
group: "XXX",
id: "xhNt11558002753379",
menu: "standard",
name: "Ola"
},
{
otherid: "3L1aa1558002753379",
age: "25",
group: "YYY",
id: "6ryVK1558002753379",
menu: "standard",
name: "Wommman"
},
{
otherid: "xhNt11558002753379",
age: "25",
group: "YYY",
id: "aL1aa1558002753312",
menu: "standard",
name: "xxxxxy"
},
{
age: "25",
group: "YYY",
id: "6ryVK1558002753379",
menu: "standard",
name: "xxxxxo"
}
,
{
otherid: "1ryVK1558002753372",
age: "25",
group: "YYY",
id: "9ryVK155a002753370",
menu: "standard",
name: "xxxxxo"
},
{
age: "25",
group: "YYY",
id: "1ryVK1558002753372",
menu: "standard",
name: "xxxxxo"
}
];
I want to sort in this way: if "id" and "otherid" is the same - let objects be next to each other. I do not know how to do it, would anyone be so good?
Like here:
const arr = [
{
age: "20",
group: "XXX",
id: "3L1aa1558002753379",
menu: "standard",
name: "Adam"
},
{
otherid: "3L1aa1558002753379",
age: "25",
group: "YYY",
id: "6ryVK1558002753379",
menu: "standard",
name: "Wommman"
},
{
age: "22",
group: "XXX",
id: "xhNt11558002753379",
menu: "standard",
name: "Ola"
},
{
otherid: "xhNt11558002753379",
age: "25",
group: "YYY",
id: "aL1aa1558002753312",
menu: "standard",
name: "xxxxxy"
},
{
age: "25",
group: "YYY",
id: "1ryVK1558002753372",
menu: "standard",
name: "xxxxxo"
},
{
otherid: "1ryVK1558002753372",
age: "25",
group: "YYY",
id: "9ryVK155a002753370",
menu: "standard",
name: "xxxxxo"
},
{
age: "25",
group: "YYY",
id: "6ryVK1558002753379",
menu: "standard",
name: "xxxxxo"
}
,
];
I tried something similar to this: Javascript sort array by two fields but it failed

You pointed out, that you only need to compute pairs and render them in a react application.
It would make much more sense to structure your data in a way your view can directly render it.
Since you are in control of the data, you don't need to generate a flat list. You can setup the pairs (of students, or whatever) using a hierarchical structure, or nested obejcts.
let students = [{name: 'Jon', id:0}, {name: 'Peter', id: 1}, {name: 'Steve', id:2}, {name: 'Joe', id: 3}]
let pairs = [{a: students [3], b: students[1]}, {a: students [2], b: students [0]}];
console.log (pairs);
Now if you want to render those pairs, you already have the data in the structure you need.
render () {
return pairs.map (pair => <Pair data={pair} />)
}
You can also flatten the pairs array and render a flat list of elements next to each other if you prefer.
let students = [{name: 'Jon', id:0}, {name: 'Peter', id: 1}, {name: 'Steve', id:2}, {name: 'Joe', id: 3}]
let pairs = [{a: students [3], b: students[1]}, {a: students [0], b: students [2]}];
const flatten = (flat, {a, b}) => [...flat, a, b];
const sorted = pairs.reduce (flatten, []);
console.log (sorted)
const Student = data => <div>{data.name}</div>
const Pair = pair => <div>
<Student data={pair.a} />
<Student data={pair.b} />
</div>
const renderFlat = () => {
return sorted.map (student => <Student data={student} />
}
const renderPairs = () => {
reutnr pairs.map (pair => <Pair data={pair} />)
}
I hope I make at least a bit sense. - Here is the sort function in any case
function sort (arr) {
let otherids = arr.reduce ((lkp, obj) => {
if (obj.otherid)
lkp [obj.otherid] = obj;
return lkp;
}, {});
let sorted = [];
for (var i=0; i < arr.length; i++) {
let obj = arr [i];
if (!!~sorted.indexOf (obj)) continue;
if (otherids [obj.id]) {
sorted.push (obj)
sorted.push(otherids[obj.id])
}
}
return sorted.concat (arr.filter (obj => !~sorted.indexOf (obj)));
}
let sorted = sort (arr);
console.log (sorted);
<script>var arr=[{age:"20",group:"XXX",id:"3L1aa1558002753379",menu:"standard",name:"Adam"},{age:"22",group:"XXX",id:"xhNt11558002753379",menu:"standard",name:"Ola"},{otherid:"3L1aa1558002753379",age:"25",group:"YYY",id:"6ryVK1558002753379",menu:"standard",name:"Wommman"},{otherid:"xhNt11558002753379",age:"25",group:"YYY",id:"aL1aa1558002753312",menu:"standard",name:"xxxxxy"},{age:"25",group:"YYY",id:"6ryVK1558002753379",menu:"standard",name:"xxxxxo"},{otherid:"1ryVK1558002753372",age:"25",group:"YYY",id:"9ryVK155a002753370",
menu:"standard",name:"xxxxxo"},{age:"25",group:"YYY",id:"1ryVK1558002753372",menu:"standard",name:"xxxxxo"}];</script>

The key to sorting strings is to use String.localeCompare(). Numbers, dates and booleans are much simpler.
Here is an example of sorting a list of Objects by two string columns - name and menu:
arr.sort(function comparerFn(L, R){
if(L.name !== R.name)
return (new String(L.name)).localeCompare(R.name)===1?1:-1
if(L.menu !== R.menu)
return (new String(L.menu)).localeCompare(R.menu)===1?1:-1
return 0
})
Reasoning for odd ===1?1:-1 syntax: localeCompare() returns 1 or 0 but sort compareFn requires either 0: (leave sorting as-is), >0: (L is before R), <0: (R is before L)

Related

Can I sort an array of objects based on a array of subarrays?

I would ask a question regarding sorting.
Let's say I have an array of objects:
let arrayToBeSorted = [
{
name:"name1",
id:"id1",
},
{
name:"name2",
id:"id2",
},
{
name:"name3",
id:"id3",
},
{
name:"name4",
id:"id4",
},
{
name:"name5",
id:"id5",
},
{
name:"name6",
id:"id6",
}];
And Let's say I have an array of sub arrays which each one is containing IDs string like that:
let sortArray = [["id2", "id1"], ["id5"], ["id6","id3","id4"]]
What I want to do is to sort the arrayToBeSorted based on the sortArray preserving each subarrays (in order to maintain an hermetic order)
This is the wanted result:
arrayToBeSorted = [
{
name:"name2",
id:"id2",
},
{
name:"name1",
id:"id1",
},
{
name:"name5",
id:"id5",
},
{
name:"name6",
id:"id6",
},
{
name:"name3",
id:"id3",
},
{
name:"name4",
id:"id4",
}];
EDIT: I tried to:
arrayToBeSorted.sort((a,b)=> for(var i=0; i<sortArray.length;i++)
{
sortArr.indexOf(a.item.id) - sortArr.indexOf(b.item.id)
});
I also thought of sorting by each array and the concat the sorted result, but no success...
Thanks!
You seem to be overcomplicating the sort operation here. Use sort() on arrayToBeSorted and get the indexOf each array element in a flat()tened copy of sortArray:
let arrayToBeSorted = [{
name: "name1",
id: "id1",
}, {
name: "name2",
id: "id2",
}, {
name: "name3",
id: "id3",
}, {
name: "name4",
id: "id4",
}, {
name: "name5",
id: "id5",
}, {
name: "name6",
id: "id6",
}];
let sortArray = [
["id2", "id1"],
["id5"],
["id6", "id3", "id4"]
];
console.log(arrayToBeSorted.sort((a, b) => sortArray.flat().indexOf(a.id) - sortArray.flat().indexOf(b.id)));
You could flat the array and build an object with wanted order and sort the array.
const
data = [{ name: "name1", id: "id1" }, { name: "name2", id: "id2" }, { name: "name3", id: "id3" }, { name: "name4", id: "id4" }, { name: "name5", id: "id5" }, { name: "name6", id: "id6" }],
sortArray = [["id2", "id1"], ["id5"], ["id6", "id3", "id4"]],
order = Object.fromEntries(sortArray.flat().map((k, i) => [k, i + 1]));
data.sort((a, b) => order[a.id] - order[b.id]);
console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use sort() based on a flattened sortArray using findIndex() or indexOf() as #esqew. You could also go a step further and pre-process sortArray and create an object ids as keys and indices of sortArray as values. Then the sort function would be based on the object as follows:
let arrayToBeSorted = [{
name: "name1",
id: "id1",
},
{
name: "name2",
id: "id2",
},
{
name: "name3",
id: "id3",
},
{
name: "name4",
id: "id4",
},
{
name: "name5",
id: "id5",
},
{
name: "name6",
id: "id6",
}
];
let sortArray = [["id2", "id1"], ["id5"], ["id6","id3","id4"]];
const flatO = Object.fromEntries( sortArray.flat().map((id,i) => [id,i]) );
const sortedArray = arrayToBeSorted.sort((a,b) => flatO[a.id] - flatO[b.id]);
console.log( sortedArray );
NOTE: This is equivalent to #NinaScholz's solution. Saw it just after I posted this. I have upvoted both #NinaScholz's and #esqew's solutions but I would take #NinaScholz's since the flat() method including the creation of the order object execute JUST ONCE.

Join value to string in array of objects

I have an array of objects in TypeScript(Angular), and I want only names from this array if details array is not empty
[
{name: "Joe", detail: []},
{name: "Kevin", detail: [{salary: 2400}] },
{name: "Peter", detail: [{salary: 2100}]}
]
I want this result Kevin,Peter in one variable
Currently I have done this
[
{name: "Joe", detail: []},
{name: "Kevin", detail: [{salary: 2400}] },
{name: "Peter", detail: [{salary: 2100}]}
].map(function(elem){
return elem.name;}).join(",")
Can anyone help me to do this in Typescript or JavaScript?
const array = [
{ name: "Joe", detail: [] },
{ name: "Kevin", detail: [{ salary: 2400 }] },
{ name: "Peter", detail: [{ salary: 2100 }] },
];
const filteredArray = array.filter((obj) => {
return obj.detail.length > 0;
});
const result = filteredArray.map((obj) => obj.name).join(", ");
console.log(result);
I do not want Joe in the result because Joe's detail is empty. I only
want kevin,Peter
You can use .reduce combines with destructure object like below to resolve it.
var data = [
{name: "Joe", detail: []},
{name: "Kevin", detail: [{salary: 2400}] },
{name: "Peter", detail: [{salary: 2100}]}
];
var result = data.reduce((acc, {name, detail}) => {
if(detail.length > 0) acc.push(name) // condition to add name into result
return acc;
}, []);
console.log(result.join(","));

how to group objects in array that are pointing at eact other

I have an array of objects, each object is pointing at another object in the "related" field.
I need to group the object with the related field so I can output them in groups on my website.
how can I group something like this
[
{
id: 1,
name: "dog",
related: "cat",
},
{
id: 2,
name: "cat",
related: "dog",
},
{
id: 3,
name: "shark",
related: "whale",
},
];
to this ?
[
[
{
id: 1,
name: "dog",
related: "cat",
},
{
id: 2,
name: "cat",
related: "dog",
},
],
[
{
id: 3,
name: "shark",
related: "whale",
},
],
];
Here i've tried this. It'll only work for two way binding relations.
const data = [
{
id: 1,
name: "dog",
related: "cat",
},
{
id: 2,
name: "cat",
related: "dog",
},
{
id: 3,
name: "shark",
related: "whale",
},
{
id: 4,
name: "whale",
related: "shark",
},
{
id: 5,
name: "test",
related: "none",
},
];
const categoriesData = (data) => {
const dataObj = data.reduce((obj, item) => {
if (!obj[item.name]) obj[item.related] = [item];
else if (obj[item.name] && obj[item.name][0].name === item.related)
obj[item.name].push(item);
return obj;
}, {});
return Object.values(dataObj);
};
console.log(categoriesData(data));
You can reduce it:
var data=[ { id: 1, name: "dog", related: "cat", }, { id: 2, name: "cat", related: "dog", }, { id: 3, name: "shark", related: "whale", }];
var result = Object.values(data.reduce((acc, elem)=>{
const key = [elem.name, elem.related].sort((a,b)=>a.localeCompare(b)).join('|');
acc[key] = [...(acc[key] || []), elem];
return acc;
},{}));
console.log(result);
this doesn't exactly answer your question, but is more of an "outside the box" solution. your data would be much simpler, straight forward and easy to manage if you simply add a "category" field.
[
{
id: 1,
name: "dog",
related: "cat",
category: 1,
},
{
id: 2,
name: "cat",
related: "dog",
category: 1,
},
{
id: 3,
name: "shark",
related: "whale",
category: 2,
},
{
id: 3,
name: "whale",
related: "shark",
category: 2,
},
];
One way of doing this would be to create a Map holding the animals. Then for each animal in this collection (that is not removed) create a group and remove it from the collection. Find related animals and add them to the same group, also removing them from the collection.
const animals = [
{ id: 1, name: "dog", related: "cat" },
{ id: 2, name: "cat", related: "dog" },
{ id: 3, name: "shark", related: "whale" },
];
const animalsMap = new Map(animals.map(animal => [animal.name, animal]));
const animalGroups = [];
for (let [,animal] of animalsMap) {
const group = Array.of(animal);
while (
animalsMap.delete(animal.name),
animal = animalsMap.get(animal.related)
) {
group.push(animal);
}
animalGroups.push(group);
}
console.log(animalGroups);
You could gather all relations first, then build groups of related values and then map the groups by filtering the data.
This approach works with longer chains of related objects.
function getGroups(k, group) {
if (seen.has(k)) return;
seen.add(k);
group.push(k);
relations[k].forEach(v => seen.has(v) || getGroups(v, group));
}
var data = [{ id: 1, name: "dog", related: "cat" }, { id: 2, name: "cat", related: "dog" }, { id: 3, name: "shark", related: "whale" }],
relations = {},
groups = [],
seen = new Set,
result;
data.forEach(({ name, related }) => {
if (!relations[name]) relations[name] = [];
relations[name].push(related);
if (!relations[related]) relations[related] = [];
relations[related].push(name);
});
Object.keys(relations).forEach(k => {
let group = [];
getGroups(k, group);
if (group.length) groups.push(group);
});
result = groups.map(group => data.filter(({ name }) => group.includes(name)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

How to edit object array?

I want to edit JavaScript object.
I have an JavaScript object like this
data_without_id = [
0: {id: 1, name: "Mary", age: null}
1: {id: 2, name: "Bob", age: 33}
2: {id: 1, name: "Kelly", age: 40}
]
I want to convert this object into this
data_without_id = [
0: {id: 1, name: "Kelly", age: 40}
1: {id: 2, name: "Bob", age: 33}
]
What I need to do is:
Group by id
Get latest value.
I tried using Array.prototype.reduce(), but I can't get the result I need...
Using the function reduce would be as follow:
The function reduce for grouping and the function Object.values for extracting the values.
let data_without_id = [ { id: 1, name: "Mary", age: null }, { id: 2, name: "Bob", age: 33 }, { id: 1, name: "Kelly", age: 40 }],
result = Object.values(data_without_id.reduce((a, {id, name, age}) => {
a[id] = {id, name, age};
return a;
}, Object.create(null)));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use .reduce by making the accumulator an object that has keys of the id. This way you can overwrite any previously seen objects which had the same id, and then use Object.values to get your array of objects:
const data_without_id = [{id: 1, name: "Mary", age: null}, {id: 2, name: "Bob", age: 33}, {id: 1, name: "Kelly", age: 40}],
res = Object.values(data_without_id.reduce((acc, obj) =>
(acc[obj.id] = obj, acc)
, {}));
console.log(res);
You could just simply use a for/of loop to create a copy of the data where the last object with a particular id will always be the object returned in the output data. This avoids the "grouping" part of the code but still returns the correct data.
const data_without_id = [
{ id: 1, name: "Mary", age: null },
{ id: 2, name: "Bob", age: 33 },
{ id: 1, name: "Kelly", age: 40 }
];
function lastId(arr) {
const out = [];
for (let obj of arr) {
// If you don't create a copy of each object the returned
// data will simply be contain _references_ to the original
// data. Changes in the original data will be reflected
// in the returned data
out[obj.id - 1] = { ...obj };
}
return out;
}
const lastIds = lastId(data_without_id);
console.log(lastIds);

Combining two arrays based on properties

I have two arrays,
let student = [{ id: "weqwe", name: "john" }, { id: "wqeqweq", name: "doe" }]
let details = [
{ id: "2qweqweq", name: "larry", oName: "john" },
{ id: "231234qa", name: "jacob", oName: "john" },
{ id: "wetyrqwte", name: "jane", oName: "doe" }
]
I need to check through each object in details array and compare it with student array (compare with oName property in details array with name property in student array) and need to add an array of the details as on object property. Also need to remove the oName, I have tried in es6 but dynamically creating array and pushing gives only the last value, Please see the below expected output,
let output = [
{
id: "weqwe",
name: "john",
details: [
{ id: "2qweqweq", name: "larry" },
{ id: "231234qa", name: "jacob" }
]
},
{
id: "wqeqweq",
name: "doe",
details: [
{ id: "wetyrqwte", name: "jane" }
]
}
]
Thanks in advance !!
try this:
let student = [{ id: "weqwe", name: "john" }, { id: "wqeqweq", name: "doe" }]
let details = [
{ id: "2qweqweq", name: "larry", oName: "john" },
{ id: "231234qa", name: "jacob", oName: "john" },
{ id: "wetyrqwte", name: "jane", oName: "doe" }
];
let output = [{
id: "weqwe",
name: "john",
details: [
{ id: "2qweqweq", name: "larry" },
{ id: "231234qa", name: "jacob" }
]
},
{
id: "wqeqweq",
name: "doe",
details: [
{ id: "wetyrqwte", name: "jane" }
]
}
];
let result = student.map((obj) => {
obj.details = details.filter(o => o.oName === obj.name).map(({oName,...other}) =>other);
return obj;
});
console.log(result);
let student = [{id:"weqwe", name:"john"}, {id:"wqeqweq", name:"doe"}]
let details = [
{id:"2qweqweq", name:"larry", oName:"john"},
{id:"231234qa", name:"jacob", oName:"john"},
{id:"wetyrqwte", name:"jane", oName:"doe"}
]
let output= student.map(student=> {
const detailsObj = details.filter(({oName})=> oName === student.name)
return {...student, details: detailsObj.map(({oName, ...other})=> other)}
})
console.log(output)

Categories

Resources