JavaScript — Сортировка массива снова с использованием другого индекса, если есть дубликаты

#javascript #arrays #sorting

#javascript #массивы #сортировка

Вопрос:

У меня есть массив, подобный этому:

 var arrValues1 = [
    [11, 58],
    [18, 45],
    [13, 23],
    [15, 68],
    [23, 32],
    [45, 45],
    [19, 68],
    [88, 68]
];
 

Чтобы отсортировать его по индексу 1, я использовал следующую функцию:

   sortIn(arr, prop) {
    return arr.sort((a, b) => {
        if (a[prop] > b[prop]) {
            return 1;
        } else if (a[prop] < b[prop]) {
            return -1;
        } else {
            return 0;
        }
    });
  } 

arrValues2.push(sortIn(arrValues1, 1));
 

Итак, я получаю этот результат:

 var arrValues2 = [
  [13, 23],
  [23, 32],
  [45, 45],
  [18, 45],
  [11, 58],
  [19, 68],
  [88, 68],
  [15, 68]
];
 

Моя проблема связана с повторяющимися значениями (45 и 68 в этом примере). Если у меня есть повторяющиеся значения, мне нужно отсортировать их с учетом значения индекса 0. Итак, конечный результат будет:

 var arrValues2 = [
  [13, 23],
  [23, 32],
  [18, 45],// > these 2 cases were reordered 
  [45, 45],//
  [11, 58],
  [15, 68],// > these 3 cases were reordered 
  [19, 68],//
  [88, 68] //
];
 

Важно сохранить все позиции, изменяя только порядок повторяющихся значений. Как я могу этого добиться?

Ответ №1:

Вы можете добавить любое количество резервных свойств сортировки в качестве параметров rest для вашей функции. Итак, вы попытаетесь выполнить сортировку по каждому и только в том случае, если все они совпадают, затем вы возвращаетесь 0 к равенству:

 var arrValues = [
    [11, 58],
    [18, 45],
    [13, 23],
    [15, 68],
    [23, 32],
    [45, 45],
    [19, 68],
    [88, 68]
];


function sortIn(arr, ...props) {
//                   ^^^^^^^^ take any amount of properties to sort by
  return arr.sort((a, b) => {
   //try sorting by each. If no `return` is reached
   //the comparison will continue with the next
    for (const prop of props) {
      if (a[prop] > b[prop]) {
          return 1;
      } else if (a[prop] < b[prop]) {
          return -1;
      }
    }
    return 0; //if no return was hit for each property, then the items are equal
  });
} 

const result = sortIn(arrValues, 1, 0);
// pass secondary sorting property  ^

//more compact display in the console
const displayResult = result.map(x => JSON.stringify(x));
console.log( displayResult ); 
 .as-console-wrapper {max-height: 100% !important; top: 0} 

Вы сохраняете универсальность вашей сортировки, потому что вы все равно можете сортировать все, что подчиняется операторам > and < :

 var data = [
  {foo: 3, bar: "a", baz: new Date("2020-01-01")},
  {foo: 2, bar: "b", baz: new Date("2020-03-03")},
  {foo: 1, bar: "b", baz: new Date("2020-03-03")},
  {foo: 1, bar: "a", baz: new Date("2020-01-01")},
  {foo: 3, bar: "b", baz: new Date("2020-03-03")},
  {foo: 2, bar: "a", baz: new Date("2020-01-01")},
];


function sortIn(arr, ...props) {
  return arr.sort((a, b) => {
    for (const prop of props) {
      if (a[prop] > b[prop]) {
          return 1;
      } else if (a[prop] < b[prop]) {
          return -1;
      }
    }
    return 0;
  });
} 

console.log( "foo --> bar --> baz" );
console.log( sortIn(data, "foo", "bar", "baz") );
console.log( "--------" );

console.log( "bar --> foo --> baz" );
console.log( sortIn(data, "bar", "foo", "baz") );
console.log( "--------" );

console.log( "baz --> foo --> bar" );
console.log( sortIn(data, "baz", "foo", "bar") );
console.log( "--------" ); 
 .as-console-wrapper {max-height: 100% !important; top: 0} 

Ответ №2:

Мы можем написать универсальный сортировщик, который принимает любое количество имен свойств, например:

 const sorter = (...props) => (a, b) => 
  props .reduce (
    (result, p) => result || (a[p] < b[p] ? -1 : a[p] > b[p] ? 1 : 0), 
    0
  )

// rearranged to demonstrate second-level sorting
const arrValues1 = [[11, 58], [45, 45], [13, 23], [19, 68], [15, 68], [23, 32], [18, 45], [88, 68]]
  
console .log (  
  arrValues1 .sort (sorter (1, 0))
) 
 .as-console-wrapper {max-height: 100% !important; top: 0} 

Если вам нужен тот же интерфейс, вы можете использовать этот сортировщик внутри sortIn , например:

 const sortIn = (arr, ...props) => 
  arr .sort (sorter (...props))

sortIn (arrValues1, 1, 0)
 

Или вы можете сложить его напрямую, как это:

 const sortIn = (arr, ...props) => 
  arr .sort ((a, b) => 
    props .reduce (
      (result, p) => result || (a[p] < b[p] ? -1 : a[p] > b[p] ? 1 : 0), 
      0
    )
  )
 

Я нахожу первую версию наиболее полезной. Я часто хочу создавать сортировщики, которые будут использоваться в нескольких местах. Но любой из них должен работать.

Ответ №3:

Вы можете передать два ключа в качестве параметров своей пользовательской функции. Затем сортировка с использованием второго реквизита, если они равны, затем проверьте первый реквизит в функции обратного вызова сортировки:

 var arrValues1 = [
    [11, 58],
    [18, 45],
    [13, 23],
    [15, 68],
    [23, 32],
    [45, 45],
    [19, 68],
    [88, 68]
];
const sortIn = (arr, prop1, prop2) => {
  return arr.sort((a, b) => a[prop1] - b[prop1] === 0 ? 
         a[prop2] - b[prop2] :
         a[prop1] - b[prop1]);
}

console.log(sortIn(arrValues1, 1, 0)) 

Ответ №4:

Помимо приведенных ответов, этот подход требует некоторых замыканий над нужной упрощенной функцией сравнения и массивом ключей для сортировки, а также сравнения ключей с коротким замыканием, если возвращаемое значение отличается от ложного, в данном случае нулевого значения.

 const
    sortBy = fn => keys => (a, b) => {
        let r;
        keys.some(k => r = fn(a[k], b[k]));
        return r;
    }
    array = [[11, 58], [18, 45], [13, 23], [15, 68], [23, 32], [45, 45], [19, 68], [88, 68]],
    ascBy = sortBy((a, b) => a - b);

array.sort(ascBy([0, 1]));
console.log(array);

array.sort(ascBy([1, 0]));
console.log(array); 
 .as-console-wrapper { max-height: 100% !important; top: 0; } 

Комментарии:

1. Неплохо. Я не стал беспокоиться о коротком замыкании в своем ответе, хотя сокращение несколько закорочено || . Более полное решение, вероятно, будет использовать произвольные функции, которые возвращают упорядоченный тип вместе ascend с / descend helpers , возможно, с API что-то вроде: const mySorter = sortBy(descend(prop('age')), ascend(prop('lastName')), ascend(prop('firstName'))) , с местом для других функций, чем просто простые свойства объекта. Но это для другого вопроса…

Ответ №5:

воспользуйтесь уничтожением массива:

 let arrValues1=[[11,58],[18,45],[13,23],[15,68],[23,32],[45,45],[19,68],[88,68]];

let result = arrValues1.sort(([i,j],[k,l]) => j === l?i-k:j-l)

console.log(result) 

Ответ №6:

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

 const arrValues2 = [
    [11, 58],
    [18, 45],
    [13, 23],
    [15, 68],
    [23, 32],
    [45, 45],
    [19, 68],
    [88, 68]
];
function sort_vals(a ,b){
    if (a[1] > b[1]) {
        return 1;
    } else if (a[1] < b[1]) {
        return -1;
    } else {
        if(a[0] > b[0]){
            return 1;
        }else if(a[0] < b[0]){
            return -1;
        } 
        return 0
    }
}
console.log(arrValues2.sort(sort_vals)) 

Ответ №7:

Вы можете упорядочить их, добавив больше логики в свой блок else следующим образом:

 sortIn(arr, prop) {
  return arr.sort((a, b) => {
    if (a[prop] > b[prop]) {
        return 1;
    } else if (a[prop] < b[prop]) {
        return -1;
    } else {
        if (a[0] > b[0]) {
           return 1
        } else {
          return -1
        }
    }
  });
}

arrValues2.push(sortIn(arrValues1, 1));
 

Ответ №8:

Вы можете сравнить снова в случае равенства, например

     sortIn(arr, prop) {
    return arr.sort((a, b) => {
        if (a[prop] > b[prop]) {
            return 1;
        } else if (a[prop] < b[prop]) {
            return -1;
        } else {
            // Compare Again
            // return 0;
           if (a[0] > b[0]) {
            return 1;
        } else if (a[0] < b[0]) {
            return -1;
        } else {
            return 0;
         }
        }
    });
  } 

arrValues2.push(sortIn(arrValues1, 1));