Как отсортировать массив объектов по нескольким полям? - PullRequest
99 голосов
/ 02 августа 2011

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

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

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

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

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

Ответы [ 29 ]

0 голосов
/ 06 февраля 2018

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

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 голосов
/ 18 июня 2015

Здесь «AffiliateDueDate» и «Title» являются столбцами, оба отсортированы в порядке возрастания.

array.sort(function(a, b) {

               if (a.AffiliateDueDate > b.AffiliateDueDate ) return 1;
               else if (a.AffiliateDueDate < b.AffiliateDueDate ) return -1;
               else if (a.Title > b.Title ) return 1;
               else if (a.Title < b.Title ) return -1;
               else return 0;
             })
0 голосов
/ 18 мая 2019

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

/** 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 голосов
/ 13 марта 2017
function sort(data, orderBy) {
        orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
        return data.sort((a, b) => {
            for (let i = 0, size = orderBy.length; i < size; i++) {
                const key = Object.keys(orderBy[i])[0],
                    o = orderBy[i][key],
                    valueA = a[key],
                    valueB = b[key];
                if (!(valueA || valueB)) {
                    console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                    return [];
                }
                if (+valueA === +valueA) {
                    return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                } else {
                    if (valueA.localeCompare(valueB) > 0) {
                        return o.toLowerCase() === 'desc' ? -1 : 1;
                    } else if (valueA.localeCompare(valueB) < 0) {
                        return o.toLowerCase() === 'desc' ? 1 : -1;
                    }
                }
            }
        });
    }

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

sort(homes, [{city : 'asc'}, {price: 'desc'}])

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 sort(data, orderBy) {
            orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
            return data.sort((a, b) => {
                for (let i = 0, size = orderBy.length; i < size; i++) {
                    const key = Object.keys(orderBy[i])[0],
                        o = orderBy[i][key],
                        valueA = a[key],
                        valueB = b[key];
                    if (!(valueA || valueB)) {
                        console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                        return [];
                    }
                    if (+valueA === +valueA) {
                        return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                    } else {
                        if (valueA.localeCompare(valueB) > 0) {
                            return o.toLowerCase() === 'desc' ? -1 : 1;
                        } else if (valueA.localeCompare(valueB) < 0) {
                            return o.toLowerCase() === 'desc' ? 1 : -1;
                        }
                    }
                }
            });
        }
console.log(sort(homes, [{city : 'asc'}, {price: 'desc'}]));
0 голосов
/ 29 августа 2014
homes.sort(function(a,b) { return a.city - b.city } );
homes.sort(function(a,b){
    if (a.city==b.city){
        return parseFloat(b.price) - parseFloat(a.price);
    } else {
        return 0;
    }
});
0 голосов
/ 21 июня 2016

Сортировка по двум полям даты и пример числового поля:

var generic_date =  new Date(2070, 1, 1);
checkDate = function(date) {
  return Date.parse(date) ? new Date(date): generic_date;
}

function sortData() {  
  data.sort(function(a,b){
    var deltaEnd = checkDate(b.end) - checkDate(a.end);
    if(deltaEnd) return deltaEnd;

    var deltaRank = a.rank - b.rank;
    if (deltaRank) return deltaRank;

    var deltaStart = checkDate(b.start) - checkDate(a.start);
    if(deltaStart) return deltaStart;

    return 0;
  });
}

http://jsfiddle.net/hcWgf/57/

0 голосов
/ 03 сентября 2016
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 голосов
/ 01 июля 2019

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

var data = [
{
    "id": 1,
    "ship": null,
    "product": "Orange",
    "quantity": 7,
    "price": 92.08,
    "discount": 0
},
{
    "id": 2,
    "ship": "2017-06-14T23:00:00.000Z".toDate(),
    "product": "Apple",
    "quantity": 22,
    "price": 184.16,
    "discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]

// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
    if (value==null || value==undefined) return null
    var cls = type(value)
    switch (cls){
        case String:
            return value.lower()
    }
    return value
}

function compare(a, b, i){
    i = i || 0
    var prop = sorts[i]
    var va = comp_val(a[prop])
    var vb = comp_val(b[prop])

    // handle what to do when both or any values are null
    if (va == null || vb == null) return true

    if ((i < sorts.length-1) && (va == vb)) {
        return compare(a, b, i+1)
    } 
    return va > vb
}

var d = data.sort(compare);
console.log(d);

Если a и b равны, он просто пробует следующее поле, пока ни одно из них не доступно.

0 голосов
/ 28 ноября 2017

Адаптация ответа @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 = '' + JSON.stringify(homes, null, '\t') + '
';
<div id="output">

</div>

Скрипка для проверки с

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...