Ordina la matrice di oggetti per chiave singola con valore data


257

Ho una matrice di oggetti con diverse coppie di valori-chiave e ho bisogno di ordinarli in base a 'updated_at':

[
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
]

Qual è il modo più efficiente per farlo?


@Topener Quel link sembra essere una domanda su PHP
David Brainer,

errore mio .. non l'ho letto correttamente
Rene Pot,

Risposte:


339

È possibile utilizzare Array.sort.

Ecco un esempio:

var arr = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

arr.sort(function(a, b) {
  var keyA = new Date(a.updated_at),
    keyB = new Date(b.updated_at);
  // Compare the 2 dates
  if (keyA < keyB) return -1;
  if (keyA > keyB) return 1;
  return 0;
});

console.log(arr);


17
Non potresti usare keyA - keyB(o forse keyB - keyA)? Gli oggetti data hanno un valueOf()metodo.
soktinpk,

ritorna a. aggiornato_at <b. aggiornato_at? 1: -1 funziona per me. Non è necessario analizzare un formato data.
oliversisson,

La capacità di fare a-bè davvero importante se si desidera evitare il codice lungo. Mentre in questo caso il codice lungo non è necessariamente negativo, penso che la verbosità renda più difficile la comprensione. Attualmente uso values.sort((a,b)=>a.attr-b.attr). Dover scrivere 5 righe ogni volta che è necessario ordinare un array diventa noioso.
AnnanFay

Questo esempio non è efficace e esegue molti calcoli non necessari. L'ordinamento può prendere qualsiasi numero positivo o negativo come risultato valido. I tuoi calcoli extra per forzarlo a 1,0, -1 non sono necessari e aggiungono semplicemente calcoli extra per esecuzione.
Patrick W. McMahon il

Funziona alla grande grazie
Andy il

158

Ho già risposto a una domanda molto simile qui: semplice funzione per ordinare una matrice di oggetti

Per quella domanda ho creato questa piccola funzione che potrebbe fare quello che vuoi:

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key]; var y = b[key];
        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}


2
Come lo invertiresti?

4
Per non distinguere tra maiuscole e minuscole, puoi aggiungere .toLowerCase () alle variabili xey
Jacob van Lingen,

4
Per invertire funzioni di ordinamento come queste, basta moltiplicare il risultato per -1:)
Svish

5
O semplicemente prendere l'output e utilizzare la array = array.reverse()funzione.
Luke Stevenson,

31

Il metodo Array.sort () ordina gli elementi di un array sul posto e restituisce l'array. Fai attenzione con Array.sort () in quanto non è immutabile . Per l'ordinamento immutabile utilizzare immutable-sort .

Questo metodo consiste nell'ordinare l'array usando il tuo attuale updated_atin formato ISO. Noi usiamonew Data(iso_string).getTime() per convertire il tempo ISO in timestamp Unix. Un timestamp Unix è un numero su cui possiamo fare semplici calcoli matematici. Sottraiamo il primo e il secondo timestamp il risultato è; se il primo timestamp è maggiore del secondo, il numero di ritorno sarà positivo. Se il secondo numero è maggiore del primo, il valore restituito sarà negativo. Se i due sono uguali, il ritorno sarà zero. Questo si allinea perfettamente con i valori di ritorno richiesti per la funzione inline.

Per ES6 :

arr.sort((a,b) => new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime());

Per ES5 :

arr.sort(function(a,b){ 
 return new Date(a.updated_at).getTime() - new Date(b.updated_at).getTime();
});

Se si modifica il updated_attimestamp per essere unix, è possibile farlo:

Per ES6 :

arr.sort((a,b) => a.updated_at - b.updated_at);

Per ES5 :

arr.sort(function(a,b){ 
 return a.updated_at - b.updated_at;
});

Al momento di questo post, i browser moderni non supportano ES6. Per usare ES6 nei browser moderni usa babel per traspilare il codice in ES5. Aspettatevi il supporto del browser per ES6 nel prossimo futuro.

Array.sort () dovrebbe recuperare un valore di ritorno di uno dei 3 possibili esiti:

  • Un numero positivo (primo elemento> secondo elemento)
  • Un numero negativo (primo elemento <secondo elemento)
  • 0 se i due elementi sono uguali

Si noti che il valore restituito sulla funzione inline può essere qualsiasi numero positivo o negativo. Array.Sort () non importa quale sia il numero di ritorno. Si preoccupa solo se il valore di ritorno è positivo, negativo o zero.

Per ordinamento immutabile: (esempio in ES6)

const sort = require('immutable-sort');
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

Puoi anche scriverlo in questo modo:

import sort from 'immutable-sort';
const array = [1, 5, 2, 4, 3];
const sortedArray = sort(array);

L'importazione da te è un nuovo modo per includere javascript in ES6 e rende il tuo codice molto pulito. Il mio preferito personale.

L'ordinamento immutabile non muta l'array di origine, ma restituisce un nuovo array. constSi consiglia l' utilizzo di dati immutabili.


19

Ecco una versione leggermente modificata della risposta di @David Brainer-Bankers che ordina in ordine alfabetico per stringa o numericamente per numero e garantisce che le parole che iniziano con le lettere maiuscole non ordinino sopra le parole che iniziano con una lettera minuscola (ad es. "Apple, Early" sarebbe visualizzato in quell'ordine).

function sortByKey(array, key) {
    return array.sort(function(a, b) {
        var x = a[key];
        var y = b[key];

        if (typeof x == "string")
        {
            x = (""+x).toLowerCase(); 
        }
        if (typeof y == "string")
        {
            y = (""+y).toLowerCase();
        }

        return ((x < y) ? -1 : ((x > y) ? 1 : 0));
    });
}

3
La soluzione proposta potrebbe dare un errore se a [chiave] eb [chiave] non sono entrambe le stringhe. Suggerisco di sostituire y = y.toLowerCase () con y = ("" + y) .toLowerCase ()
user8074

sort può accettare qualsiasi numero positivo o negativo come reso valido. Sei calcoli extra per forzarlo a 1,0, -1 non è necessario. Hai complicato un semplice valore di ritorno. È meglio non aggiungere calcoli extra che non fanno nulla.
Patrick W. McMahon il

15

Usa il trattino basso js o lodash,

var arrObj = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];

arrObj = _.sortBy(arrObj,"updated_at");

_.sortBy() restituisce un nuovo array

consultare http://underscorejs.org/#sortBy e i documenti lodash https://lodash.com/docs#sortBy


6

Con il supporto ES2015 è possibile:

foo.sort((a, b) => a.updated_at < b.updated_at ? -1 : 1)

1
non è necessario per l'inline se sostituire <con - e rimuovere "? -1: 1 "otterrai un ritorno valido. In questo esempio vengono spostati elementi che possono essere uguali e quindi potrebbero dare risultati imprevisti. Per oggetti uguali dovrebbe essere restituito uno 0.
Patrick W. McMahon

Grazie per
avermi

se updated_at è un tempo ISO, questo non funzionerà. Questo esempio presuppone i timestamp di Unix ma i dati pubblicati dall'OP erano in formato ISO. Quindi dovresti convertire in timestamp Unix per fare un confronto. Questo può essere fatto con new Date(iso_str).getTime()questo restituirà un timestamp Unix.
Patrick W. McMahon,

5

Dati importati

[
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    }
]

PER Ordine crescente

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA < keyB) return -1;
    if(keyA > keyB) return 1;
    return 0;
});

Esempio per Ordine asc

[
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    }
]

PER Ordine discendente

arr.sort(function(a, b){
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA > keyB) return -1;
    if(keyA < keyB) return 1;
    return 0;
});

Esempio per Desc Order

[
    {
        "gameStatus": "1",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 11:32:04"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-20 10:54:09"
    },
    {
        "gameStatus": "1",
        "userId": "7118ed61-d8d9-4098-a81b-484158806d21",
        "created_at": "2018-12-20 10:50:48"
    },
    {
        "gameStatus": "0",
        "userId": "1a2fefb0-5ae2-47eb-82ff-d1b2cc27875a",
        "created_at": "2018-12-19 18:46:22"
    },
    {
        "gameStatus": "2",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:35:40"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 18:08:24"
    },
    {
        "gameStatus": "0",
        "userId": "c02cfb18-ae66-430b-9524-67d9dd8f6a50",
        "created_at": "2018-12-19 10:42:53"
    }
]

3

Come afferma questa risposta, è possibile utilizzare Array.sort.

arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)})

arr = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }
];
arr.sort(function(a,b){return new Date(a.updated_at) - new Date(b.updated_at)});
console.log(arr);


2

Solo un altro modo, più matematico , di fare la stessa cosa ma più breve :

arr.sort(function(a, b){
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

o nello stile slick lambda arrow:

arr.sort((a, b) => {
    var diff = new Date(a.updated_at) - new Date(b.updated_at);
    return diff/(Math.abs(diff)||1);
});

Questo metodo può essere eseguito con qualsiasi input numerico


risposta sottovalutata
two7s_clash


2

Ho creato una funzione di ordinamento in Typescript che possiamo usare per cercare stringhe, date e numeri in array di oggetti. Può anche ordinare su più campi.

export type SortType = 'string' | 'number' | 'date';
export type SortingOrder = 'asc' | 'desc';

export interface SortOptions {
  sortByKey: string;
  sortType?: SortType;
  sortingOrder?: SortingOrder;
}


class CustomSorting {
    static sortArrayOfObjects(fields: SortOptions[] = [{sortByKey: 'value', sortType: 'string', sortingOrder: 'desc'}]) {
        return (a, b) => fields
          .map((field) => {
            if (!a[field.sortByKey] || !b[field.sortByKey]) {
              return 0;
            }

            const direction = field.sortingOrder === 'asc' ? 1 : -1;

            let firstValue;
            let secondValue;

            if (field.sortType === 'string') {
              firstValue = a[field.sortByKey].toUpperCase();
              secondValue = b[field.sortByKey].toUpperCase();
            } else if (field.sortType === 'number') {
              firstValue = parseInt(a[field.sortByKey], 10);
              secondValue = parseInt(b[field.sortByKey], 10);
            } else if (field.sortType === 'date') {
              firstValue = new Date(a[field.sortByKey]);
              secondValue = new Date(b[field.sortByKey]);
            }
            return firstValue > secondValue ? direction : firstValue < secondValue ? -(direction) : 0;

          })
          .reduce((pos, neg) => pos ? pos : neg, 0);
      }
    }
}

Uso:

const sortOptions = [{
      sortByKey: 'anyKey',
      sortType: 'string',
      sortingOrder: 'asc',
    }];

arrayOfObjects.sort(CustomSorting.sortArrayOfObjects(sortOptions));

1

L'ordinamento in base a una data in formato ISO può essere costoso, a meno che non si limiti i client ai browser più recenti e migliori, che possono creare il timestamp corretto per data analizzando la stringa.

Se sei sicuro del tuo input e sai che sarà sempre aaaa-mm-ggThh: mm: ss e GMT (Z) puoi estrarre le cifre da ciascun membro e confrontarle come numeri interi

array.sort(function(a,b){
    return a.updated_at.replace(/\D+/g,'')-b.updated_at.replace(/\D+/g,'');
});

Se la data potrebbe essere formattata in modo diverso, potrebbe essere necessario aggiungere qualcosa per le persone con problemi iso:

Date.fromISO: function(s){
    var day, tz,
    rx=/^(\d{4}\-\d\d\-\d\d([tT ][\d:\.]*)?)([zZ]|([+\-])(\d\d):(\d\d))?$/,
    p= rx.exec(s) || [];
    if(p[1]){
        day= p[1].split(/\D/).map(function(itm){
            return parseInt(itm, 10) || 0;
        });
        day[1]-= 1;
        day= new Date(Date.UTC.apply(Date, day));
        if(!day.getDate()) return NaN;
        if(p[5]){
            tz= (parseInt(p[5], 10)*60);
            if(p[6]) tz+= parseInt(p[6], 10);
            if(p[4]== '+') tz*= -1;
            if(tz) day.setUTCMinutes(day.getUTCMinutes()+ tz);
        }
        return day;
    }
    return NaN;
}
if(!Array.prototype.map){
    Array.prototype.map= function(fun, scope){
        var T= this, L= T.length, A= Array(L), i= 0;
        if(typeof fun== 'function'){
            while(i< L){
                if(i in T){
                    A[i]= fun.call(scope, T[i], i, T);
                }
                ++i;
            }
            return A;
        }
    }
}
}

2
Non potresti semplicemente usare Date.parse?
Rocket Hazmat,

1

Per completezza, ecco una possibile breve implementazione generica di sortBy:

function sortBy(list, keyFunc) {
  return list.sort((a,b) => keyFunc(a) - keyFunc(b));
}

sortBy([{"key": 2}, {"key": 1}], o => o["key"])

Si noti che questo utilizza il metodo di ordinamento delle matrici che ordina in posizione. per una copia puoi usare arr.concat () o arr.slice (0) o un metodo simile per creare una copia.


1

Con questo possiamo passare una funzione chiave da utilizzare per l'ordinamento

Array.prototype.sortBy = function(key_func, reverse=false){
    return this.sort( (a, b) => {
        var keyA = key_func(a),
            keyB = key_func(b);
        if(keyA < keyB) return reverse? 1: -1;
        if(keyA > keyB) return reverse? -1: 1;
        return 0;
    }); 
}

Quindi per esempio se abbiamo

var arr = [ {date: "01/12/00", balls: {red: "a8",  blue: 10}},
            {date: "12/13/05", balls: {red: "d6" , blue: 11}},
            {date: "03/02/04", balls: {red: "c4" , blue: 15}} ]

Possiamo fare

arr.sortBy(el => el.balls.red)
/* would result in
[ {date: "01/12/00", balls: {red: "a8", blue: 10}},
  {date: "03/02/04", balls: {red: "c4", blue: 15}},
  {date: "12/13/05", balls: {red: "d6", blue: 11}} ]
*/

o

arr.sortBy(el => new Date(el.date), true)   // second argument to reverse it
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},
  {date: "03/02/04", balls: {red: "c4", blue:15}},
  {date: "01/12/00", balls: {red: "a8", blue:10}} ]
*/

o

arr.sortBy(el => el.balls.blue + parseInt(el.balls.red[1]))
/* would result in
[ {date: "12/13/05", balls: {red: "d6", blue:11}},    // red + blue= 17
  {date: "01/12/00", balls: {red: "a8", blue:10}},    // red + blue= 18
  {date: "03/02/04", balls: {red: "c4", blue:15}} ]   // red + blue= 19
*/

1

È possibile utilizzare la libreria di utilità Lodash per risolvere questo problema (è una libreria abbastanza efficiente):

const data = [{
    "updated_at": "2012-01-01T06:25:24Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-09T11:25:13Z",
    "foo": "bar"
  },
  {
    "updated_at": "2012-01-05T04:13:24Z",
    "foo": "bar"
  }
]

const ordered = _.orderBy(
  data,
  function(item) {
    return item.updated_at;
  }
);

console.log(ordered)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

Puoi trovare la documentazione qui: https://lodash.com/docs/4.17.15#orderBy


0

Puoi creare una chiusura e passarla in questo modo qui è il mio esempio che funziona

$.get('https://data.seattle.gov/resource/3k2p-39jp.json?$limit=10&$where=within_circle(incident_location, 47.594972, -122.331518, 1609.34)', 
  function(responce) {

    var filter = 'event_clearance_group', //sort by key group name
    data = responce; 

    var compare = function (filter) {
        return function (a,b) {
            var a = a[filter],
                b = b[filter];

            if (a < b) {
                return -1;
            } else if (a > b) {
                return 1;
            } else {
                return 0;
            }
        };
    };

    filter = compare(filter); //set filter

    console.log(data.sort(filter));
});

0
var months = [
    {
        "updated_at" : "2012-01-01T06:25:24Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-09T11:25:13Z",
        "foo" : "bar"
    },
    {
        "updated_at" : "2012-01-05T04:13:24Z",
        "foo" : "bar"
    }];
months.sort((a, b)=>{
    var keyA = new Date(a.updated_at),
        keyB = new Date(b.updated_at);
    // Compare the 2 dates
    if(keyA < keyB) return -1;
    if(keyA > keyB) return 1;
    return 0;
});
console.log(months);

0
  • Utilizzare Array.sort()per ordinare un array
  • Clonare l'array usando l' operatore spread ( ) per rendere pura la funzione
  • Ordina per chiave desiderata ( updated_at)
  • Converti la stringa della data in oggetto data
  • Array.sort() funziona sottraendo due proprietà dall'elemento corrente e successivo se si tratta di un numero / oggetto su cui è possibile eseguire operazioni aritmiche
const input = [
  {
    updated_at: '2012-01-01T06:25:24Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-09T11:25:13Z',
    foo: 'bar',
  },
  {
    updated_at: '2012-01-05T04:13:24Z',
    foo: 'bar',
  }
];

const sortByUpdatedAt = (items) => [...items].sort((itemA, itemB) => new Date(itemA.updated_at) - new Date(itemB.updated_at));

const output = sortByUpdatedAt(input);

console.log(input);
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' } ]
*/
console.log(output)
/*
[ { updated_at: '2012-01-01T06:25:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-05T04:13:24Z', foo: 'bar' }, 
  { updated_at: '2012-01-09T11:25:13Z', foo: 'bar' } ]
*/

0

Devo affrontare la stessa cosa, quindi gestisco questo con un generico perché e creo una funzione per questo:

//example:
//array: [{name: 'idan', workerType: '3'}, {name: 'stas', workerType: '5'}, {name: 'kirill', workerType: '2'}]
//keyField: 'workerType'
// keysArray: ['4', '3', '2', '5', '6']
sortByArrayOfKeys = (array, keyField, keysArray) => {
    array.sort((a, b) => {
        const aIndex = keysArray.indexOf(a[keyField])
        const bIndex = keysArray.indexOf(b[keyField])
        if (aIndex < bIndex) return -1;
        if (aIndex > bIndex) return 1;
        return 0;
    })
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.