Натуральная сортировка массива объектов по нескольким условиям

Имеем массив объектов:

const data = [
            {"name": "John", "email": "[email protected]", "age": 36},
            {"name": "Alex", "email": "[email protected]", "age": 18},
            {"name": "Mari", "email": "[email protected]", "age": 18},
            {"name": "John", "email": "[email protected]", "age": 19},
            {"name": "Jane", "email": "[email protected]", "age": 25},
        ];

Массив условия для сортировки может состоять из разного количества элементов и в разной последовательности:

const condition = { "sort_by": [ "name", "age"]};

Мне нужно написать функцию, которая выполнит эту сортировку, причем при применение данных 2 условий мы сначала должны отсортировать по ключу name, а потом отсортированный массив еще отсортировать по ключу age, не теряя результата первой сортировки, т.е. после первого прохода получаем вот такой массив (сортировка по name):

 [
      { name: 'Alex', email: '[email protected]', age: 18 },
      { name: 'Jane', email: '[email protected]', age: 25 },
      { name: 'John', email: '[email protected]', age: 36 },
      { name: 'John', email: '[email protected]', age: 19 },
      { name: 'Mari', email: '[email protected]', age: 18 }
    ]

конечный результат должен быть вот таким:

[
      { name: 'Alex', email: '[email protected]', age: 18 },
      { name: 'Jane', email: '[email protected]', age: 25 },
      { name: 'John', email: '[email protected]', age: 19 },
      { name: 'John', email: '[email protected]', age: 36 },
      { name: 'Mari', email: '[email protected]', age: 18 }
    ]

Мы меняем только 2 записи местами, 2-х John по возрасту, но остальная сортировка не меняется. Написал вот такой код:

const sortBy = (cond, arr) => {
    const keys = cond.sort_by;
    const inputArr = JSON.parse(JSON.stringify(arr))
    let sortedArray;
    keys.forEach(key => {
        sortedArray = inputArr.sort((a, b) => {
            return (typeof (a[key]) === 'number'  && typeof (b[key]) === 'number')
                     ? a[key] - b[key] : a[key].localeCompare(b[key])
        })
    })
    return sortedArray
}

Но такой код делает сортировку 2 раза (сначала сортирует по name, а потом этот же массив сортирует по age и результат первой сортировки пропадает), и я получаю не тот результат что мне нужен. Подскажите как исправить эту проблему?

Если использовать такие условия сортировки:

sortedArray = inputArr.sort((a, b) => (a.name.localeCompare(b.name) || a.age - b.age))

то все работает как нужно, за исключением того, что я указываю хардкором ключи по которым сортирую, а они могут быть разные))

const data = [
                {"name": "John", "email": "[email protected]", "age": 36},
                {"name": "Alex", "email": "[email protected]", "age": 18},
                {"name": "Mari", "email": "[email protected]", "age": 18},
                {"name": "John", "email": "[email protected]", "age": 19},
                {"name": "Jane", "email": "[email protected]", "age": 25},
            ];
            
const condition = { "sort_by": [ "name", "age"]};

const sortBy = (cond, arr) => {

    const keys = cond.sort_by;
    const inputArr = JSON.parse(JSON.stringify(arr))
    let sortedArray;
    keys.forEach(key => {

        //sortedArray = inputArr.sort((a, b) => a[key].localeCompare(b[key]) || a[key] - b[key] )

        //sortedArray = inputArr.sort((a, b) => (a.name.localeCompare(b.name) || a.age - b.age))
        sortedArray = inputArr.sort((a, b) => {
            //console.log(typeof (a[key]) === 'number' && typeof (b[key]) === 'number')
            return (typeof (a[key]) === 'number'  && typeof (b[key]) === 'number') ? a[key] - b[key] : a[key].localeCompare(b[key])
        })
    })
    return sortedArray
}
console.log(sortBy(condition, data))


Ответы (1 шт):

Автор решения: EzioMercer

Можете попробовать так:

const data = [
  {"name": "John", "email": "[email protected]", "age": 36},
  {"name": "Alex", "email": "[email protected]", "age": 18},
  {"name": "Mari", "email": "[email protected]", "age": 18},
  {"name": "John", "email": "[email protected]", "age": 19},
  {"name": "Jane", "email": "[email protected]", "age": 25},
];

const compare = (a, b) => {
  if (a === b) return 0;
  if (a > b) return 1;
  if (a < b) return -1;
}

const sortBy = (fields, arr) => {

  return arr.sort((a, b) => {
    for (field of fields) {          
      const result = compare(a[field], b[field]);
      
      if (result === 0) continue;
      
      return result;
    }
    
    return 0;
  })
}

console.log(sortBy(["name", "age"], data));
console.log('---------------------------------------------------------')
console.log(sortBy(["name"], data));
console.log('---------------------------------------------------------')
console.log(sortBy(["email"], data));

→ Ссылка