3

I have below array of objects in javascript

[
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'}]

Now I am in need to apply nested sorting.

A user could sort the above collection by name, age, city.

Suppose User asks to sort by name, the sorted array would look alike.

[
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'}
{'name' : 'Mohan', 'age': 50 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'}]

Now User clicks on to sort by age (asc), but it should not disturb the above array.

It should look alike

[
{'name' : 'Aditya', 'age': 5 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 10 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'}]

But if suppose user asks to sort the above sort desc by age so it should look alike

 [
{'name' : 'Aditya', 'age': 10 , 'city' : 'a'},
{'name' : 'Aditya', 'age': 5 , 'city' : 'b'}
{'name' : 'Ram', 'age': 10 , 'city' : 'a'},
{'name' : 'Shyam', 'age': 5 , 'city' : 'a'}

This is how my current function to sort looks alike.

let compareObjects = (key, order = 'asc') => {
return function (a, b) {
    if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
        // property doesn't exist on either object
        return 0;
    }

    const varA = (typeof a[key] === 'string') ?
        a[key].toUpperCase() : a[key];
    const varB = (typeof b[key] === 'string') ?
        b[key].toUpperCase() : b[key];

    let comparison = 0;
    if (varA > varB) {
        comparison = 1;
    } else if (varA < varB) {
        comparison = -1;
    }
    return (
        (order == 'desc') ? (comparison * -1) : comparison
    );
}}


 arrOfObjects.sort(compareObjects('age'));

But I have no clue how can I apply nested sorting?

Thanks!

2
  • And what is wrong with the current solution? Commented Apr 24, 2019 at 7:22
  • While this question is about pure JS solution, you might want to look into lodash.orderBy: lodash.com/docs/4.17.11#orderBy. They solved the issue neatly. Commented Apr 24, 2019 at 7:40

4 Answers 4

1

The method compareObjects can accept an array of { key: order } objects (sortBy). Then loop the sortBy array with for...of, extract the key and order, and compare untill the result is not 0, or return 0 if they're all equal.

const compareStr = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
const compareNumber = (a, b) => a - b

const compareBy = new Map([
  ['name', compareStr],
  ['age', compareNumber],
  ['city', compareStr],
])

const compareObjects = sortBy => (a, b) => {
  for (const srt of sortBy) { // iterate sortBy
    const [key, order] = Object.entries(srt)[0]; // extract key|order pairs
    const sorter = compareBy.get(key) // get the sorter from the Map

    if (!sorter || !(key in a) || !(key in b)) continue // if no sorter or if key doesn't exist in either object continue to the next sorter

    const score = sorter(a[key], b[key]) // sort the current values

    if (score === 0) continue; // if score is 0 continue to next sorter

    return score * (order === 'asc' ? 1 : -1) // return the score multiplied by -1 for non asc values
  }

  return 0;
}

const data = [{"name":"Ram","age":10,"city":"a"},{"name":"Shyam","age":5,"city":"a"},{"name":"Aditya","age":10,"city":"b"},{"name":"Aditya","age":5,"city":"a"}]

const result = data.sort(compareObjects([
  { age: 'desc' }, 
  { name: 'asc' }
]))

console.log(result)

Sign up to request clarification or add additional context in comments.

6 Comments

I checked but it disturbs the position of the name. Which it should not
Please elaborate. In the example it sorts by multiple fields - by age desc, and then by name asc. On each selection of fields, you need to pass all sort fields to the function.
@Kgn-web data.sort(compareObjects({ name: 'asc', age: 'desc' })). Reordering the sorting order will make it work as you want.
@OriDrori I think that using Object to store sorting order like { age: 'desc', name: 'asc' } is not good enough since Javascript does not guarantee property orders of Object.
I checked. it seems fruitful. Many Thanks!!
|
1

Array.prototype.sort() modifies the original array. It doesnot copy the array.

Return Value

The sorted array. Note that the array is sorted in place, and no copy is made.

You can create a clone using spread operator.Spread operator will make shallow copy of array.

Another way you can make the code better is use 1 and -1 instead of asc and desc and multiply it with the result of sort() callback.

Note: Spread Operator will only make a shallow copy means that objects will still have the reference. If you want to deep copy use JSON.parse(JSON.stringify())

const arr = [
  {'name' : 'Ram', 'age': 10 , 'city' : 'a'},
  {'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
  {'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
  {'name' : 'Aditya', 'age': 5 , 'city' : 'a'}
]

function sortBy(arr,key,order=1){
  arr = [...arr]
  return arr.sort((a,b) => (a[key] > b[key] ? 1 : -1) * order)
}

console.log(sortBy(arr,'name'))
console.log(sortBy(arr,'age',-1))

Comments

0

You can also use sortBy from lodash.

const arr = [
  {'name' : 'Ram', 'age': 10 , 'city' : 'a'},
  {'name' : 'Shyam', 'age': 5 , 'city' : 'a'},
  {'name' : 'Aditya', 'age': 10 , 'city' : 'b'},
  {'name' : 'Aditya', 'age': 5 , 'city' : 'a'}
]

console.log('sortBy Name:', _.sortBy(arr, ['name']))
console.log('sortBy age:', _.sortBy(arr, ['age']))

console.log('sortBy City:', _.sortBy(arr, ['city']))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>

Comments

0

You need to hand over all sorting criteria at one and get the sorted array back.

This approach uses an array of arrays with keys and direction. If the direction is not given, i sorts ascending.

function sortBy(columns) {
    var cols = columns.map(([key, d]) => ({ key, dir: -(d === 'desc') || 1 }));
    return function (a, b) {
        var v;

        cols.some(({ key, dir }) => {
            var aVal = a[key],
                bVal = b[key];

            if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return v = 0;
            if (typeof aVal === 'string' && typeof bVal === 'string') {
                aVal = aVal.toUpperCase();
                bVal = bVal.toUpperCase();
            }
            return v = dir * (aVal > bVal || -(aVal < bVal));
        })

        return v;
    };
}

var data = [{ name: "Ram", age: 10, city: "a" }, { name: "Shyam", age: 5, city: "a" }, { name: "Aditya", age: 10, city: "b" }, { name: "Aditya", age: 5, city: "a" }]

console.log(data.sort(sortBy([['name'], ['age', 'desc']])));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

Your Answer

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.