2

I have a persons list and I sort it by the column in sortColumn.

const persons = [{
 name: "alireza",
 family: "seif",
 other: {
 age: 28,
 rate: 30
 }
 },
 {
 name: "sara",
 family: "niki",
 other: {
 age: 15,
 rate: 15
 }
 },
 {
 name: "fateme",
 family: "azizy",
 other: {
 age: 27,
 rate: 35
 }
 }
];
const sortColumn = {
 path: "name",
 order: "asc"
};
persons.sort((person1, person2) =>
 person1[sortColumn.path] > person2[sortColumn.path] ?
 sortColumn.order === "asc" ?
 1 :
 -1 :
 person2[sortColumn.path] > person1[sortColumn.path] ?
 sortColumn.order === "asc" ?
 -1 :
 1 :
 0
);
console.log(persons);

If sortColumn.path is "name" and order is "asc" (or "desc"), the sort function works correctly. But how can I sort by "other.age"?

Thanks.

Alessio Cantarella
5,2413 gold badges31 silver badges37 bronze badges
asked Jan 17, 2020 at 17:54

3 Answers 3

2

You could take a functin which return a sort function, depending on the sort order.

This sorting function uses another function for getting a value from an object by reducing the splitted path to the value.

const
 sortBy = ({ order = 'asc', path }) => order === 'asc'
 ? (a, b) => ((a, b) => a > b || -(a < b))(getValue(a, path), getValue(b, path))
 : (a, b) => ((a, b) => b > a || -(b < a))(getValue(a, path), getValue(b, path)),
 getValue = (object, keys) => keys.split('.').reduce((o, k) => o[k], object),
 persons = [{ name: "sara", family: "niki", other: { age: 15, rate: 15 } }, { name: "alireza", family: "seif", other: { age: 28, rate: 30 } }, { name: "fateme", family: "azizy", other: { age: 27, rate: 35 } }];
console.log(persons.sort(sortBy({ path: "name", order: "asc" })));
console.log(persons.sort(sortBy({ path: "other.rate", order: "desc" })));
.as-console-wrapper { max-height: 100% !important; top: 0; }

answered Jan 17, 2020 at 19:15
5
  • thank you. in code console.log(persons.sort(sortBy({ path: "name", order: "asc" }))); must sort [{name:'alireza'...},{name:'fateme'...},{name:'sara'..}} Commented Jan 18, 2020 at 6:41
  • @A.R.SEIF, i see the result in the wanted order. Commented Jan 18, 2020 at 9:03
  • thank you.i am copy code in console browser .and result not equals.thanks Commented Jan 18, 2020 at 9:37
  • you may have a problem with console, bcause this is sometimes async to the data. for comparing take JSON.stringify and check against. Commented Jan 18, 2020 at 9:38
  • some more to read: stackoverflow.com/questions/23392111/console-log-async-or-sync Commented Jan 18, 2020 at 9:47
2

If you want to do sort by that deep property age, you can't use sortColumn exactly as you have... Instead, one option is to modify it by making it an array of properties, like so:

sortColumn = { path:["other","age"], order:"asc" }

This way, you'd also have to modify the sort function - as seen in the example below:

const persons = [
 {name:"alireza",family:"seif",other:{age:28,rate:30}},
 {name:"fateme",family:"azizy",other:{age:27,rate:35}},
 {name:"sara",family:"niki",other:{age:15,rate:15}}
]
const sortColumn = { path:["other","age"], order:"asc" }
persons.sort((person1, person2) =>
 person1[sortColumn.path[0]][sortColumn.path[1]] > person2[sortColumn.path[0]][sortColumn.path[1]] 
 ? sortColumn.order === "asc"
 ? 1
 : -1
 : person2[sortColumn.path[0]][sortColumn.path[1]] > person1[sortColumn.path[0]][sortColumn.path[1]] 
 ? sortColumn.order === "asc"
 ? -1
 : 1
 : 0
);
console.log(persons)

However, this approach doesn't work to sort by "name" since this sort function sorts your data by some piece of data that is two-layers deep (inside "other", then "age"). Here's a modification you can make to the sort function which lets you sort by any properties, any number of layers deep into your data:

const persons = [
 {name:"alireza",family:"seif",other:{age:28,rate:30}},
 {name:"fateme",family:"azizy",other:{age:27,rate:35}},
 {name:"sara",family:"niki",other:{age:15,rate:15}}
]
const sortColumn = { path:["name"], order:"asc" }
persons.sort((person1, person2) => {
 // get array of paths
 const sortPaths = sortColumn.path;
 // get values to sort by
 const val1 = sortPaths.reduce((acc, path) => {
 if (acc[path]) return acc[path]
 else alert(`can't find prop ${path} in person1`);
 }, person1)
 const val2 = sortPaths.reduce((acc, path) => {
 if (acc[path]) return acc[path]
 else alert(`can't find prop ${path} in person2`);
 }, person2)
 return val1 > val2 
 ? sortColumn.order === "asc"
 ? 1
 : -1
 : val2 > val1 
 ? sortColumn.order === "asc"
 ? -1
 : 1
 : 0
});
console.log(persons)
https://stackoverflow.com/questions/59792589/sort-object-in-object-by-javascript/59792918#

As you can see in this second snippet, you can now search by a shallow property "name" by using path: ["name"], but if you want to sort by a deep value, just add both properties to the path array like this: path: ["other", "age"]

Hope this helps!

answered Jan 17, 2020 at 18:20
2

The main thing to do is to make your code expect 'path' be a function that selects a property's value, instead of a string pointing to a property name. That way it's much more flexible.

const sortColumn = {
 path: p => p.other.age,
 order: 'desc'
};

However, if 'persons' is not the only object you'd like to do this with, you can further abstract such a function for general use with any array, such as this:

function sorter (array, path, order) {
 array.sort((a,b) => {
 let result = 
 path(a) > path(b) ? 1
 : path(a) < path(b) ? -1
 : 0;
 if (order === "desc")
 result = -result;
 return result;
 });
}

Use it like this:

sorter(persons, p => p.other.age, 'desc');

Expand and run the snippet below to see it in action:

function sorter (array, path, order) {
 array.sort((a,b) => {
 let result = 
 path(a) > path(b) ? 1
 : path(a) < path(b) ? -1
 : 0;
 if (order === "desc")
 result = -result;
 return result;
 });
 
}
const persons = [
 {name: "alireza", family: "seif", other: {age: 28, rate: 30}},
 {name: "sara", family: "niki", other: {age: 15, rate: 15}},
 {name: "fateme", family: "azizy", other: {age: 27, rate: 35}}
];
// convenience function
let sortAndLog = (array, path, order) => {
 sorter(array, path, order);
 console.log(array.map(path));
}
sortAndLog(persons, p => p.name, "asc");
sortAndLog(persons, p => p.name, "desc");
sortAndLog(persons, p => p.other.age, "asc");
sortAndLog(persons, p => p.other.age, "desc");

answered Jan 17, 2020 at 18:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.