Kgn-web
Kgn-web

Reputation: 7555

How to apply nested sorting in javascript

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!

Upvotes: 3

Views: 95

Answers (4)

Ori Drori
Ori Drori

Reputation: 191946

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)

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386550

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; }

Upvotes: 0

Ruhul Amin
Ruhul Amin

Reputation: 1779

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>

Upvotes: 0

Maheer Ali
Maheer Ali

Reputation: 36564

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))

Upvotes: 1

Related Questions