it-swarm.com.ru

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

Из этого исходного вопроса , как бы я применил сортировку к нескольким полям?

Используя эту слегка адаптированную структуру, как бы я отсортировал город (по возрастанию) и затем по цене (по убыванию)?

var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "Zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "Zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "Zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "Zip":"00010",
     "price":"962500"}
    ];

Мне понравился факт, чем был дан ответ , который обеспечил общий подход. Там, где я планирую использовать этот код, мне придется сортировать даты так же, как и другие вещи. Способность «заправлять» объект казалась удобной, если не немного громоздкой.

Я пытался встроить этот ответ в хороший пример, но мне не очень повезло.

86
Mike

Метод многомерной сортировки, на основе этого ответа :

Update: вот «оптимизированная» версия. Это делает намного больше предварительной обработки и создает функцию сравнения для каждой опции сортировки заранее. Может потребоваться больше памяти (так как в ней хранится функция для каждой опции сортировки, но она должна быть лучше подготовлена, так как не нужно определять правильные настройки во время сравнения. Я не выполнял профилирование).

var sort_by;

(function() {
    // utility functions
    var default_cmp = function(a, b) {
            if (a == b) return 0;
            return a < b ? -1 : 1;
        },
        getCmpFunc = function(primer, reverse) {
            var dfc = default_cmp, // closer in scope
                cmp = default_cmp;
            if (primer) {
                cmp = function(a, b) {
                    return dfc(primer(a), primer(b));
                };
            }
            if (reverse) {
                return function(a, b) {
                    return -1 * cmp(a, b);
                };
            }
            return cmp;
        };

    // actual implementation
    sort_by = function() {
        var fields = [],
            n_fields = arguments.length,
            field, name, reverse, cmp;

        // preprocess sorting options
        for (var i = 0; i < n_fields; i++) {
            field = arguments[i];
            if (typeof field === 'string') {
                name = field;
                cmp = default_cmp;
            }
            else {
                name = field.name;
                cmp = getCmpFunc(field.primer, field.reverse);
            }
            fields.Push({
                name: name,
                cmp: cmp
            });
        }

        // final comparison function
        return function(A, B) {
            var a, b, name, result;
            for (var i = 0; i < n_fields; i++) {
                result = 0;
                field = fields[i];
                name = field.name;

                result = field.cmp(A[name], B[name]);
                if (result !== 0) break;
            }
            return result;
        }
    }
}());

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

homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));

DEMO


Оригинальная функция:

var sort_by = function() {
   var fields = [].slice.call(arguments),
       n_fields = fields.length;

   return function(A,B) {
       var a, b, field, key, primer, reverse, result, i;

       for(i = 0; i < n_fields; i++) {
           result = 0;
           field = fields[i];

           key = typeof field === 'string' ? field : field.name;

           a = A[key];
           b = B[key];

           if (typeof field.primer  !== 'undefined'){
               a = field.primer(a);
               b = field.primer(b);
           }

           reverse = (field.reverse) ? -1 : 1;

           if (a<b) result = reverse * -1;
           if (a>b) result = reverse * 1;
           if(result !== 0) break;
       }
       return result;
   }
};

DEMO

65
Felix Kling

для не универсального, простого решения вашей точной проблемы:

homes.sort(
   function(a, b) {          
      if (a.city === b.city) {
         // Price is only important when cities are the same
         return b.price - a.price;
      }
      return a.city > b.city ? 1 : -1;
   });
75
Snowburnt

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

var homes = [
    {"h_id":"3", "city":"Dallas", "state":"TX","Zip":"75201","price":"162500"},
    {"h_id":"4","city":"Bevery Hills", "state":"CA", "Zip":"90210", "price":"319250"},
    {"h_id":"6", "city":"Dallas", "state":"TX", "Zip":"75000", "price":"556699"},
    {"h_id":"5", "city":"New York", "state":"NY", "Zip":"00010", "price":"962500"}
    ];

homes.sort(fieldSorter(['city', '-price']));
// homes.sort(fieldSorter(['Zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
    return function (a, b) {
        return fields
            .map(function (o) {
                var dir = 1;
                if (o[0] === '-') {
                   dir = -1;
                   o=o.substring(1);
                }
                if (a[o] > b[o]) return dir;
                if (a[o] < b[o]) return -(dir);
                return 0;
            })
            .reduce(function firstNonZeroValue (p,n) {
                return p ? p : n;
            }, 0);
    };
}

Правка: в ES6 это даже короче!

"use strict";
const fieldSorter = (fields) => (a, b) => fields.map(o => {
    let dir = 1;
    if (o[0] === '-') { dir = -1; o=o.substring(1); }
    return a[o] > b[o] ? dir : a[o] < b[o] ? -(dir) : 0;
}).reduce((p, n) => p ? p : n, 0);

const homes = [{"h_id":"3", "city":"Dallas", "state":"TX","Zip":"75201","price":162500},     {"h_id":"4","city":"Bevery Hills", "state":"CA", "Zip":"90210", "price":319250},{"h_id":"6", "city":"Dallas", "state":"TX", "Zip":"75000", "price":556699},{"h_id":"5", "city":"New York", "state":"NY", "Zip":"00010", "price":962500}];
const sortedHomes = homes.sort(fieldSorter(['state', '-price']));

document.write('<pre>' + JSON.stringify(sortedHomes, null, '\t') + '</pre>')

32
chriskelly

Сегодня я сделал довольно универсальный многофункциональный сортировщик. Вы можете взглянуть на thenBy.js здесь: https://github.com/Teun/thenBy.js

Это позволяет вам использовать стандартный Array.sort, но со стилем firstBy (). ThenBy (). ThenBy (). Это намного меньше кода и сложности, чем решения, опубликованные выше.

29
Teun D

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

Первый аргумент должен быть массивом, содержащим объекты . Последующие аргументы должны быть разделенным запятыми списком строк, которые ссылаются на различные свойства объекта для сортировки. Последний аргумент (который является необязательным) - это логическое значение, позволяющее выбрать, выполнять ли сортировку с учетом регистра - используйте true для сортировки с учетом регистра.

Функция сортирует каждое свойство/ключ по возрастанию по умолчанию. Если вы хотите, чтобы определенный ключ сортировался в порядке убывания, вместо этого передайте массив в следующем формате: ['property_name', true].

Вот несколько примеров использования функции с последующим объяснением (где homes - массив, содержащий объекты):

objSort(homes, 'city') -> сортировка по городу (по возрастанию, без учета регистра)

objSort(homes, ['city', true]) -> сортировка по городу (по убыванию, без учета регистра)

objSort(homes, 'city', true) -> сортировка по городу, затем цена (по возрастанию, регистр с учетом )

objSort(homes, 'city', 'price') -> сортировка по городу, затем по цене (по возрастанию, без учета регистра)

objSort(homes, 'city', ['price', true]) -> сортировка по городу (по возрастанию), затем по цене (по убыванию), без учета регистра)

И без дальнейших церемоний, вот функция:

function objSort() {
    var args = arguments,
        array = args[0],
        case_sensitive, keys_length, key, desc, a, b, i;

    if (typeof arguments[arguments.length - 1] === 'boolean') {
        case_sensitive = arguments[arguments.length - 1];
        keys_length = arguments.length - 1;
    } else {
        case_sensitive = false;
        keys_length = arguments.length;
    }

    return array.sort(function (obj1, obj2) {
        for (i = 1; i < keys_length; i++) {
            key = args[i];
            if (typeof key !== 'string') {
                desc = key[1];
                key = key[0];
                a = obj1[args[i][0]];
                b = obj2[args[i][0]];
            } else {
                desc = false;
                a = obj1[args[i]];
                b = obj2[args[i]];
            }

            if (case_sensitive === false && typeof a === 'string') {
                a = a.toLowerCase();
                b = b.toLowerCase();
            }

            if (! desc) {
                if (a < b) return -1;
                if (a > b) return 1;
            } else {
                if (a > b) return -1;
                if (a < b) return 1;
            }
        }
        return 0;
    });
} //end of objSort() function

А вот некоторые примеры данных:

var homes = [{
    "h_id": "3",
    "city": "Dallas",
    "state": "TX",
    "Zip": "75201",
    "price": 162500
}, {
    "h_id": "4",
    "city": "Bevery Hills",
    "state": "CA",
    "Zip": "90210",
    "price": 1000000
}, {
    "h_id": "5",
    "city": "new york",
    "state": "NY",
    "Zip": "00010",
    "price": 1000000
}, {
    "h_id": "6",
    "city": "Dallas",
    "state": "TX",
    "Zip": "85000",
    "price": 300000
}, {
    "h_id": "7",
    "city": "New York",
    "state": "NY",
    "Zip": "00020",
    "price": 345000
}];
11
jake

Вот еще один, который, возможно, ближе к вашей идее о синтаксисе

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {}; // primers are optional

    properties = properties.map(function(prop) {
        if( !(prop instanceof Array) ) {
            prop = [prop, 'asc']
        }
        if( prop[1].toLowerCase() == 'desc' ) {
            prop[1] = -1;
        } else {
            prop[1] = 1;
        }
        return prop;
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.Push( prop[1] * valueCmp(aValue, bValue) );
            arr2.Push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}

// just for fun use this to reverse the city name when sorting
function demoPrimer(str) {
    return str.split('').reverse().join('');
}

// Example
sortObjects(homes, ['city', ['price', 'desc']], {city: demoPrimer});

Демо: http://jsfiddle.net/Nq4dk/2/


Правка: просто для удовольствия, вот вариант , который просто принимает строку, похожую на sql, так что вы можете сделать sortObjects(homes, "city, price desc")

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {};

    properties = properties.split(/\s*,\s*/).map(function(prop) {
        prop = prop.match(/^([^\s]+)(\s*desc)?/i);
        if( prop[2] && prop[2].toLowerCase() === 'desc' ) {
            return [prop[1] , -1];
        } else {
            return [prop[1] , 1];
        }
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.Push( prop[1] * valueCmp(aValue, bValue) );
            arr2.Push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}
7
Flambino

Это полный обман, но я думаю, что он добавляет ценность к этому вопросу, потому что это в основном консервированная библиотечная функция, которую вы можете использовать «из коробки».

Если ваш код имеет доступ к lodash или lodash-совместимой библиотеке, такой как underscore, тогда вы можете использовать метод _.sortBy. Фрагмент ниже скопирован непосредственно из документации lodash .

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

var users = [
  { 'user': 'fred',   'age': 48 },
  { 'user': 'barney', 'age': 36 },
  { 'user': 'fred',   'age': 40 },
  { 'user': 'barney', 'age': 34 }
];

_.sortBy(users, [function(o) { return o.user; }]);
 // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
5
Guy

Проще один:

var someArray = [...];

function generateSortFn(props) {
    return function (a, b) {
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var name = prop.name;
            var reverse = prop.reverse;
            if (a[name] < b[name])
                return reverse ? 1 : -1;
            if (a[name] > b[name])
                return reverse ? -1 : 1;
        }
        return 0;
    };
};

someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));
4
Ravshan Samandarov

Мне нравится подход SnowBurnt, но ему нужен твик, чтобы проверить эквивалентность по городу, а НЕ разницу.

homes.sort(
   function(a,b){
      if (a.city==b.city){
         return (b.price-a.price);
      } else {
         return (a.city-b.city);
      }
   });
3
james kenny

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

var data = [{ h_id: "3", city: "Dallas", state: "TX", Zip: "75201", price: "162500" }, { h_id: "4", city: "Bevery Hills", state: "CA", Zip: "90210", price: "319250" }, { h_id: "6", city: "Dallas", state: "TX", Zip: "75000", price: "556699" }, { h_id: "5", city: "New York", state: "NY", Zip: "00010", price: "962500" }];

data.sort(function (a, b) {
    return a.city.localeCompare(b.city) || b.price - a.price;
});

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

1
Nina Scholz

Вот мое решение, основанное на идиоме преобразования Шварца , надеюсь, вы найдете его полезным.

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

Вот пример того, как его использовать:

let games = [
  { name: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));
1
a8m

Вот общая версия решения @ Snowburnt:

var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
    for(var i=0; i<sortarray.length; i++){
        retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
        if (sortarray[i].direction == "desc") {
            retval = retval * -1;
        }
        if (retval !== 0) {
            return retval;
        }
    }
}


})

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

Я протестировал его (на самом деле с немного более сложной логикой сортировки) на 5000 записях, и он сделал это в мгновение ока. Если вы на самом деле загружаете более 1000 записей в клиент, вам, вероятно, следует использовать сортировку и фильтрацию на стороне сервера.

Этот код не обрабатывает чувствительность к регистру, но я оставляю читателю справиться с этой тривиальной модификацией.

1
HisDivineShadow

По-другому

var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "Zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "Zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "Zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "Zip":"00010",
     "price":"962500"}
    ];
function sortBy(ar) {
  return ar.sort((a, b) => a.city === b.city ?
      b.price.toString().localeCompare(a.price) :
      a.city.toString().localeCompare(b.city));
}
console.log(sortBy(homes));

1
Mihai

Просто еще один вариант. Попробуйте использовать следующую служебную функцию:

/** Performs comparing of two items by specified properties
 * @param  {Array} props for sorting ['name'], ['value', 'city'], ['-date']
 * to set descending order on object property just add '-' at the begining of property
 */
export const compareBy = (...props) => (a, b) => {
  for (let i = 0; i < props.length; i++) {
    const ascValue = props[i].startsWith('-') ? -1 : 1;
    const prop = props[i].startsWith('-') ? props[i].substr(1) : props[i];
    if (a[prop] !== b[prop]) {
      return a[prop] > b[prop] ? ascValue : -ascValue;
    }
  }
  return 0;
};

Пример использования (в вашем случае):

homes.sort(compareBy('city', '-price'));

Следует отметить, что эта функция может быть еще более обобщенной, чтобы можно было использовать вложенные свойства, такие как 'address.city' или 'style.size.width' и т.д.

0
Dmitry Anch

Адаптация ответа @chriskelly. 


Большинство ответов не учитывают, что цена не будет отсортирована должным образом, если значение будет в десяти тысячах и ниже или более миллиона. JS сортирует по алфавиту. Здесь довольно хорошо ответили, Почему JavaScript не может сортировать "5, 10, 1" а здесь Как правильно отсортировать массив целых чисел .

В конечном итоге мы должны провести некоторую оценку, если поле или узел, по которому мы сортируем, является числом. Я не говорю, что использование parseInt() в этом случае является правильным ответом, отсортированные результаты более важны.

var homes = [{
  "h_id": "2",
  "city": "Dallas",
  "state": "TX",
  "Zip": "75201",
  "price": "62500"
}, {
  "h_id": "1",
  "city": "Dallas",
  "state": "TX",
  "Zip": "75201",
  "price": "62510"
}, {
  "h_id": "3",
  "city": "Dallas",
  "state": "TX",
  "Zip": "75201",
  "price": "162500"
}, {
  "h_id": "4",
  "city": "Bevery Hills",
  "state": "CA",
  "Zip": "90210",
  "price": "319250"
}, {
  "h_id": "6",
  "city": "Dallas",
  "state": "TX",
  "Zip": "75000",
  "price": "556699"
}, {
  "h_id": "5",
  "city": "New York",
  "state": "NY",
  "Zip": "00010",
  "price": "962500"
}];

homes.sort(fieldSorter(['price']));
// homes.sort(fieldSorter(['Zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
  return function(a, b) {
    return fields
      .map(function(o) {
        var dir = 1;
        if (o[0] === '-') {
          dir = -1;
          o = o.substring(1);
        }
        if (!parseInt(a[o]) && !parseInt(b[o])) {
          if (a[o] > b[o]) return dir;
          if (a[o] < b[o]) return -(dir);
          return 0;
        } else {
          return dir > 0 ? a[o] - b[o] : b[o] - a[o];
        }
      })
      .reduce(function firstNonZeroValue(p, n) {
        return p ? p : n;
      }, 0);
  };
}
document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
<div id="output">

</div>


Скрипка для проверки с помощью

0
Mark Carpenter Jr

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

function sortByPriority(data, priorities) {
  if (priorities.length == 0) {
    return data;
  }

  const nextPriority = priorities[0];
  const remainingPriorities = priorities.slice(1);

  const matched = data.filter(item => item.hasOwnProperty(nextPriority));
  const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));

  return sortByPriority(matched, remainingPriorities)
    .sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
    .concat(sortByPriority(remainingData, remainingPriorities));
}

И вот пример того, как вы его используете.

const data = [
  { id: 1,                         mediumPriority: 'bbb', lowestPriority: 'ggg' },
  { id: 2, highestPriority: 'bbb', mediumPriority: 'ccc', lowestPriority: 'ggg' },
  { id: 3,                         mediumPriority: 'aaa', lowestPriority: 'ggg' },
];

const priorities = [
  'highestPriority',
  'mediumPriority',
  'lowestPriority'
];


const sorted = sortByPriority(data, priorities);

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

0
Steztric

Вот общая многомерная сортировка, позволяющая менять направление и/или отображать на каждом уровне.

Написано на TypeScript. Для Javascript, проверьте это JSFiddle

Код

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

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

Сортировка массива людей по фамилии, а затем по имени:

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

Сортируйте коды языков по их имени , а не по коду языка (см. map), затем по по убыванию версии (см. reverse).

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));
0
Joshua Hansen

простое решение:

items.sort((a, b) => {
  const compare_name = a.name.localeCompare(b.name);
  const compare_title = a.title.localeCompare(b.title);
  const compare_city = a.city.localeCompare(b.city);

  return compare_name || compare_title || compare_city;
});

Если вам нужно отсортировать больше полей, добавьте больше ||

0
Leonardo Filipe

Вот расширяемый способ сортировки по нескольким полям.

homes.sort(function(left, right) {
    var city_order = left.city.localeCompare(right.city);
    var price_order = parseInt(left.price) - parseInt(right.price);
    return city_order || -price_order;
});

Заметки

  • Функция a.localeCompare(b)универсально поддерживается и возвращает -1,0,1, если a<b, a==b, a>b соответственно.
  • Вычитание работает на числовых полях.
  • || в последней строке дает city приоритет перед price.
  • Отрицать обратный порядок в любом поле, как в -price_order
  • Сравнение дат , var date_order = new Date(left.date) - new Date(right.date); работает как числа, потому что date math с 1970 года превращается в миллисекунды.
  • Добавьте поля в цепочку or, return city_order || -price_order || date_order;
0
Bob Stein

Я думаю, что это может быть самый простой способ сделать это.

https://coderwall.com/p/ebqhca/javascript-sort-by-two-fields

Это действительно просто, и я попробовал это с 3 различными парами ключ-значение, и это прекрасно работало.

Вот простой пример, посмотрите на ссылку для более подробной информации

testSort(data) {
    return data.sort(
        a['nameOne'] > b['nameOne'] ? 1
        : b['nameOne'] > a['nameOne'] ? -1 : 0 ||
        a['date'] > b['date'] ||
        a['number'] - b['number']
    );
}
0
JDinar
function sortMultiFields(prop){
    return function(a,b){
        for(i=0;i<prop.length;i++)
        {
            var reg = /^\d+$/;
            var x=1;
            var field1=prop[i];
            if(prop[i].indexOf("-")==0)
            {
                field1=prop[i].substr(1,prop[i].length);
                x=-x;
            }

            if(reg.test(a[field1]))
            {
                a[field1]=parseFloat(a[field1]);
                b[field1]=parseFloat(b[field1]);
            }
            if( a[field1] > b[field1])
                return x;
            else if(a[field1] < b[field1])
                return -x;
        }
    }
}

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

homes.sort(sortMultiFields(["city","-price"]));

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

0
Nikhil sHETH

Вот мой для вашей справки, с примером:

function msort(arr, ...compFns) {
  let fn = compFns[0];
  arr = [].concat(arr);
  let arr1 = [];
  while (arr.length > 0) {
    let arr2 = arr.splice(0, 1);
    for (let i = arr.length; i > 0;) {
      if (fn(arr2[0], arr[--i]) === 0) {
        arr2 = arr2.concat(arr.splice(i, 1));
      }
    }
    arr1.Push(arr2);
  }

  arr1.sort(function (a, b) {
    return fn(a[0], b[0]);
  });

  compFns = compFns.slice(1);
  let res = [];
  arr1.map(a1 => {
    if (compFns.length > 0) a1 = msort(a1, ...compFns);
    a1.map(a2 => res.Push(a2));
  });
  return res;
}

let tstArr = [{ id: 1, sex: 'o' }, { id: 2, sex: 'm' }, { id: 3, sex: 'm' }, { id: 4, sex: 'f' }, { id: 5, sex: 'm' }, { id: 6, sex: 'o' }, { id: 7, sex: 'f' }];

function tstFn1(a, b) {
  if (a.sex > b.sex) return 1;
  else if (a.sex < b.sex) return -1;
  return 0;
}

function tstFn2(a, b) {
  if (a.id > b.id) return -1;
  else if (a.id < b.id) return 1;
  return 0;
}

console.log(JSON.stringify(msort(tstArr, tstFn1, tstFn2)));
//output:
//[{"id":7,"sex":"f"},{"id":4,"sex":"f"},{"id":5,"sex":"m"},{"id":3,"sex":"m"},{"id":2,"sex":"m"},{"id":6,"sex":"o"},{"id":1,"sex":"o"}]
0
zipper

Я искал что-то похожее и закончил с этим:

Сначала у нас есть одна или несколько сортирующих функций, всегда возвращающих 0, 1 или -1:

const sortByTitle = (a, b): number => 
  a.title === b.title ? 0 : a.title > b.title ? 1 : -1;

Вы можете создавать больше функций для каждого другого свойства, по которому вы хотите сортировать.

Тогда у меня есть функция, которая объединяет эти функции сортировки в одну:

const createSorter = (...sorters) => (a, b) =>
  sorters.reduce(
    (d, fn) => (d === 0 ? fn(a, b) : d),
    0
  );

Это может быть использовано для объединения вышеуказанных функций сортировки в удобочитаемом виде:

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

Когда функция сортировки возвращает 0, вызывается следующая функция сортировки для дальнейшей сортировки.

0
Soesah