Массив сортировки Javascript по двум полям

88
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);      
    return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;
});

Таким образом, приведенный выше код сортирует массив по gsize - от наименьшего к наибольшему. Работает хорошо. Но если размер такой же, я бы хотел отсортировать его по свечению.

Спасибо.

отметка
источник
Функция сортировки реагирует на положительный, отрицательный или нулевой результат. так что вы можете просто написать: "return aSize - bSize". это будет более простой и читаемый код.

Ответы:

107
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    var aLow = a.glow;
    var bLow = b.glow;
    console.log(aLow + " | " + bLow);

    if(aSize == bSize)
    {
        return (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0;
    }
    else
    {
        return (aSize < bSize) ? -1 : 1;
    }
});
Крис Эберли
источник
170
grouperArray.sort(function (a, b) {   
    return a.gsize - b.gsize || a.glow - b.glow;
});

более короткая версия

анморозов23
источник
отличный короткий путь! помогло мне собрать более сложное решение .. stackoverflow.com/questions/6101475/…
Джозеф Пуарье
3
Красиво и чисто! Единственное, работает только для чисел.
Афанасий Куракин
Вы можете объяснить здесь логику?!. У меня получилось, sort an array with a key's value firstа потомsort the result with another key's value
KTM
1
@KTM Логика следующая: если оба gsize равны, то первая часть условия равна 0, что считается ложным, а вторая часть условия выполняется.
Scalpweb
@Scalpweb Да :) так что это работает для сортировки массива с любым количеством ключей один за другим, верно ?! Хороший трюк
KTM
35
grouperArray.sort((a, b) => a.gsize - b.gsize || a.glow - b.glow);

Еще более короткая версия с использованием синтаксиса стрелок!

Винорт
источник
3
Самый лаконичный и расширяемый, идеальный!
Laurent
1
Еще короче, уберите пробелы:grouperArray.sort((a,b)=>a.gsize-b.gsize||a.glow-b.glow);
Captain Fantastic
если вы хотите , чтобы понять , почему это работает , Вы могли бы хотеть смотреть на medium.com/@safareli/pss-ordering-is-a-monoid-61a4029387e
Safareli
14

Я понимаю, что это было задано некоторое время назад, но я подумал, что добавлю свое решение.

Эта функция динамически генерирует методы сортировки. просто укажите имя каждого сортируемого дочернего свойства с добавлением +/-, чтобы указать возрастающий или убывающий порядок. Очень многоразовый, и ему не нужно ничего знать о структуре данных, которую вы собрали. Можно сделать идиотское доказательство - но в этом нет необходимости.

function getSortMethod(){
    var _args = Array.prototype.slice.call(arguments);
    return function(a, b){
        for(var x in _args){
            var ax = a[_args[x].substring(1)];
            var bx = b[_args[x].substring(1)];
            var cx;

            ax = typeof ax == "string" ? ax.toLowerCase() : ax / 1;
            bx = typeof bx == "string" ? bx.toLowerCase() : bx / 1;

            if(_args[x].substring(0,1) == "-"){cx = ax; ax = bx; bx = cx;}
            if(ax != bx){return ax < bx ? -1 : 1;}
        }
    }
}

пример использования:

items.sort (getSortMethod ('- цена', '+ приоритет', '+ имя'));

это будет сортировать itemsс наименьшим priceпервым, с привязкой к элементу с наибольшим priority. дальнейшие связи разрываются товаромname

где items - это массив вроде:

var items = [
    { name: "z - test item", price: "99.99", priority: 0, reviews: 309, rating: 2 },
    { name: "z - test item", price: "1.99", priority: 0, reviews: 11, rating: 0.5 },
    { name: "y - test item", price: "99.99", priority: 1, reviews: 99, rating: 1 },
    { name: "y - test item", price: "0", priority: 1, reviews: 394, rating: 3.5 },
    { name: "x - test item", price: "0", priority: 2, reviews: 249, rating: 0.5 } ...
];

живая демонстрация: http://gregtaff.com/misc/multi_field_sort/

РЕДАКТИРОВАТЬ: исправлена ​​проблема с Chrome.

Nihlton
источник
Это великолепно
Azure
Гениальный ответ!
Мариус
для машинописного текста (чтобы не получить error TS2554: Expected 0 arguments, but got ..) используйте синтаксис здесь: stackoverflow.com/a/4116634/5287221
Chananel P
6

Я думаю, что тернарный оператор ((aSize < bSize) ? -1 : (aSize > bSize) ? 1 : 0;)вас запутал. Вы должны проверить ссылку, чтобы лучше понять это.

А пока вот ваш код полностью продуман if / else.

grouperArray.sort(function (a, b) {
    if (a.gsize < b.gsize)
    {
        return -1;
    }
    else if (a.gsize > b.gsize)
    {
        return 1;
    }
    else
    {
        if (a.glow < b.glow)
        {
            return -1;
        }
        else if (a.glow > b.glow)
        {
            return 1;
        }
        return 0;
    }
});
Джон Грин
источник
6

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

Array.prototype.sortBy = function (propertyName, sortDirection) {

    var sortArguments = arguments;
    this.sort(function (objA, objB) {

        var result = 0;
        for (var argIndex = 0; argIndex < sortArguments.length && result === 0; argIndex += 2) {

            var propertyName = sortArguments[argIndex];
            result = (objA[propertyName] < objB[propertyName]) ? -1 : (objA[propertyName] > objB[propertyName]) ? 1 : 0;

            //Reverse if sort order is false (DESC)
            result *= !sortArguments[argIndex + 1] ? 1 : -1;
        }
        return result;
    });

}

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

var arr = [{
  LastName: "Doe",
  FirstName: "John",
  Age: 28
}, {
  LastName: "Doe",
  FirstName: "Jane",
  Age: 28
}, {
  LastName: "Foo",
  FirstName: "John",
  Age: 30
}];

arr.sortBy("LastName", true, "FirstName", true, "Age", false);
//Will return Jane Doe / John Doe / John Foo

arr.sortBy("Age", false, "LastName", true, "FirstName", false);
//Will return John Foo / John Doe / Jane Doe
The_Black_Smurf
источник
3
grouperArray.sort(function (a, b) {
  var aSize = a.gsize;
  var bSize = b.gsize;
  var aLow = a.glow;
  var bLow = b.glow;
  console.log(aLow + " | " + bLow);      
  return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : ( (aLow < bLow ) ? -1 : (aLow > bLow ) ? 1 : 0 );
});
силекс
источник
3
grouperArray.sort(function (a, b) {
     var aSize = a.gsize;     
     var bSize = b.gsize;     
     var aLow = a.glow;
     var bLow = b.glow;
     console.log(aLow + " | " + bLow);
     return (aSize < bSize) ? -1 : (aSize > bSize) ? 1 : (aLow < bLow) ? -1 : (aLow > bLow) ? 1 : 0); }); 
Тим Уильямс
источник
3

Вот реализация, которая использует рекурсию для сортировки по любому количеству полей сортировки от 1 до бесконечности. Вы передаете ему массив результатов, который представляет собой массив объектов результата для сортировки, и массив сортировки, который представляет собой массив объектов сортировки, определяющих сортировку. Каждый объект сортировки должен иметь ключ «select» для имени ключа, по которому он сортирует, и ключ «order», который представляет собой строку, указывающую «по возрастанию» или «по убыванию».

sortMultiCompare = (a, b, sorts) => {
    let select = sorts[0].select
    let order = sorts[0].order
    if (a[select] < b[select]) {
        return order == 'ascending' ? -1 : 1
    } 
    if (a[select] > b[select]) {
        return order == 'ascending' ? 1 : -1
    }
    if(sorts.length > 1) {
        let remainingSorts = sorts.slice(1)
        return this.sortMultiCompare(a, b, remainingSorts)
    }
    return 0
}

sortResults = (results, sorts) => {
    return results.sort((a, b) => {
        return this.sortMultiCompare(a, b, sorts)
    })
}

// example inputs
const results = [
    {
        "LastName": "Doe",
        "FirstName": "John",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Doe",
        "FirstName": "Jane",
        "MiddleName": "Bill"
    },
    {
        "LastName": "Johnson",
        "FirstName": "Kevin",
        "MiddleName": "Bill"
    }
]

const sorts = [
    {
        "select": "LastName",
        "order": "ascending"
    },
    {
        "select": "FirstName",
        "order": "ascending"
    },
    {
        "select": "MiddleName",
        "order": "ascending"
    }    
]

// call the function like this:
let sortedResults = sortResults(results, sorts)
Бенджамин Портман
источник
2

Динамический способ сделать это с НЕСКОЛЬКИМИ клавишами:

  • фильтровать уникальные значения из каждого столбца / ключа сортировки
  • привести в порядок или отменить
  • добавить весовые значения ширины нуля для каждого объекта на основе значений ключей indexOf (значение)
  • сортировать с использованием вычисленных весов

введите описание изображения здесь

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

Использование:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

введите описание изображения здесь

Леонардо Филипе
источник
1

Это то, что я использую

function sort(a, b) {
    var _a = "".concat(a.size, a.glow);
    var _b = "".concat(b.size, b.glow);
    return _a < _b;
}

объедините два элемента в виде строки, и они будут отсортированы по строковому значению. Если вы хотите, вы можете заключить _a и _b в parseInt, чтобы сравнить их как числа, если вы знаете, что они будут числовыми.

блок-петля
источник
1

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

Пример входных данных ( id2 - приоритетный ключ сортировки):

const arr = [
    {id: 1},
    {id: 2, id2: 3},
    {id: 4},
    {id: 3},
    {id: 10, id2: 2},
    {id: 7},
    {id: 6, id2: 1},
    {id: 5},
    {id: 9, id2: 2},
    {id: 8},
];

И вывод должен быть:

[ { id: 6, id2: 1 },
  { id: 9, id2: 2 },
  { id: 10, id2: 2 },
  { id: 2, id2: 3 },
  { id: 1 },
  { id: 3 },
  { id: 4 },
  { id: 5 },
  { id: 7 },
  { id: 8 } ]

Функция компаратора будет такой:

arr.sort((a,b) => {
  if(a.id2 || b.id2) {
    if(a.id2 && b.id2) {
      if(a.id2 === b.id2) {
        return a.id - b.id;
      }
      return a.id2 - b.id2;
    }
    return a.id2 ? -1 : 1;
  }
  return a.id - b.id
});

PS В случае, если .id из .id2 могут быть нулями, рассмотрите возможность использования typeof.

Делиаз
источник
0
grouperArray.sort(
  function(a,b){return a.gsize == b.gsize ? a.glow - b.glow : a.gsize - b.gsize}
);
ic3b3rg
источник
0
grouperArray.sort(function (a, b) {
    var aSize = a.gsize;
    var bSize = b.gsize;
    if (aSize !== aSize)
        return aSize - bSize;
    return a.glow - b.glow;
});

не проверял, но думаю, что должно работать.

Ксандер
источник
0

В моем случае я сортирую список уведомлений по параметру "важно" и по "дате".

  • Шаг 1. Я фильтрую уведомления по «важным» и «неважным»

    let importantNotifications = notifications.filter(
            (notification) => notification.isImportant);
    
      let unImportantNotifications = notifications.filter(
            (notification) => !notification.isImportant);
    
  • шаг 2: я сортирую их по дате

      sortByDate = (notifications) => {
      return notifications.sort((notificationOne, notificationTwo) => {
        return notificationOne.date - notificationTwo.date;
      });
    };
    
  • шаг 3: объедините их

    [
        ...this.sortByDate(importantNotifications),
        ...this.sortByDate(unImportantNotifications),
      ];
    
Phm Hùng
источник