Ordenar una matriz de objetos por una sola clave con valor de fecha


257

Tengo una matriz de objetos con varios pares de valores clave, y necesito ordenarlos según '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"
    }
]

¿Cuál es la forma más eficiente de hacerlo?


@Topener Ese enlace parece ser una pregunta sobre PHP
David Brainer

mi error ... no lo leí correctamente
Rene Pot

Respuestas:


339

Puedes usar Array.sort.

Aquí hay un ejemplo:

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
¿No podrías usar keyA - keyB(o posiblemente keyB - keyA)? Los objetos de fecha tienen un valueOf()método.
soktinpk

volver a. updated_at <b. updated_at? 1: -1 funciona para mí. No es necesario analizar en un formato de fecha.
oliversisson el

La capacidad de hacer a-bes realmente importante si desea evitar el código largo. Si bien el código largo no es necesariamente malo en este caso, creo que la verbosidad hace que sea más difícil de entender. Actualmente uso values.sort((a,b)=>a.attr-b.attr). Tener que escribir 5 líneas cada vez que necesita ordenar una matriz se vuelve tedioso.
AnnanFay

Este ejemplo no es efectivo y hace muchos cálculos innecesarios. Ordenar puede tomar cualquier número positivo o negativo como resultado válido. Sus cálculos adicionales para forzarlo a ser 1,0, -1 no son necesarios y simplemente agrega cálculos adicionales por ejecución.
Patrick W. McMahon

Funciona muy bien gracias
Andy

158

Ya respondí una pregunta muy similar aquí: función simple para ordenar una matriz de objetos

Para esa pregunta, creé esta pequeña función que podría hacer lo que quieras:

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));
    });
}

11

2
¿Cómo revertirías esto?

44
Para que no distinga entre mayúsculas y minúsculas, puede agregar .toLowerCase () a las variables x e y
Jacob van Lingen

44
Para invertir funciones de clasificación como estas, simplemente multiplique el resultado por -1:)
Svish

55
O simplemente tome la salida y use la array = array.reverse()función.
Luke Stevenson

31

El método Array.sort () ordena los elementos de una matriz en su lugar y devuelve la matriz. Tenga cuidado con Array.sort () ya que no es inmutable . Para una ordenación inmutable, use immutable-sort .

Este método consiste en ordenar la matriz usando su actual updated_aten formato ISO. Utilizamos new Data(iso_string).getTime()para convertir el tiempo ISO a la marca de tiempo de Unix. Una marca de tiempo Unix es un número en el que podemos hacer cálculos matemáticos simples. Restamos la primera y segunda marca de tiempo que es el resultado; Si la primera marca de tiempo es mayor que la segunda, el número de retorno será positivo. Si el segundo número es mayor que el primero, el valor de retorno será negativo. Si los dos son iguales, el rendimiento será cero. Esto se alinea perfectamente con los valores de retorno requeridos para la función en línea.

Para ES6 :

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

Para ES5 :

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

Si cambia su updated_atmarca de tiempo de Unix, puede hacer esto:

Para ES6 :

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

Para ES5 :

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

En el momento de esta publicación, los navegadores modernos no son compatibles con ES6. Para usar ES6 en navegadores modernos, use babel para transpilar el código a ES5. Espere soporte de navegador para ES6 en un futuro próximo.

Array.sort () debería recuperar un valor de retorno de uno de los 3 resultados posibles:

  • Un número positivo (primer elemento> segundo elemento)
  • Un número negativo (primer elemento <segundo elemento)
  • 0 si los dos elementos son iguales

Tenga en cuenta que el valor de retorno en la función en línea puede ser cualquier número positivo o negativo. Array.Sort () no le importa cuál es el número de retorno. Solo le importa si el valor de retorno es positivo, negativo o cero.

Para el tipo inmutable: (ejemplo en ES6)

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

También puedes escribirlo de esta manera:

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

La importación desde que ve es una nueva forma de incluir JavaScript en ES6 y hace que su código se vea muy limpio. Mi favorito personal

La ordenación inmutable no muta la matriz de origen, sino que devuelve una nueva matriz. El uso constse recomienda en los datos inmutable.


19

Aquí hay una versión ligeramente modificada de la respuesta de @David Brainer-Bankers que se ordena alfabéticamente por cadena, o numéricamente por número, y asegura que las palabras que comienzan con mayúsculas no se ordenan por encima de las palabras que comienzan con una letra minúscula (por ejemplo, "manzana, temprano" se mostrará en ese orden).

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 solución propuesta podría dar un error si a [clave] yb [clave] no son ambas cadenas. Sugiero reemplazar y = y.toLowerCase () con y = ("" + y) .toLowerCase ()
user8074

sort puede aceptar cualquier número positivo o negativo como una devolución válida. Sus cálculos adicionales para forzarlo a ser 1,0, -1 no es necesario. Has complicado un simple valor de retorno. Es mejor no agregar cálculos adicionales que no hagan nada.
Patrick W. McMahon


6

Con el soporte de ES2015 se puede hacer:

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

1
no es necesario el en línea si reemplaza el <con - y elimina '? -1: 1 "obtendrá una devolución válida. Este ejemplo mueve elementos que pueden ser iguales y, por lo tanto, podrían dar resultados inesperados. Por elementos iguales se debe devolver un 0.
Patrick W. McMahon

Gracias por explicar
knowbody

si updated_at es un tiempo ISO, esto no funcionará. Este ejemplo supone marcas de tiempo de Unix, pero el OP publicó datos que estaban en formato ISO. Por lo tanto, tendría que convertir a marcas de tiempo Unix para hacer una comparación. Esto se puede hacer con new Date(iso_str).getTime()esto devolverá una marca de tiempo Unix.
Patrick W. McMahon

5

Datos importados

[
    {
        "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"
    }
]

PARA orden ascendente

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;
});

Ejemplo de orden de 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"
    }
]

PARA orden descendente

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;
});

Ejemplo de orden Desc

[
    {
        "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

Como dice esta respuesta, puede usar 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 otra forma más matemática de hacer lo mismo pero más corta :

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

o en el elegante estilo de flecha lambda:

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

Este método se puede hacer con cualquier entrada numérica


respuesta sin calificación
two7s_clash


2

He creado una función de clasificación en Typecript que podemos usar para buscar cadenas, fechas y números en una matriz de objetos. También puede ordenar en múltiples campos.

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

Ordenar por una fecha con formato ISO puede ser costoso, a menos que limite los clientes a los navegadores más recientes y mejores, que pueden crear la marca de tiempo correcta al analizar la cadena.

Si está seguro de su entrada y sabe que siempre será aaaa-mm-ddThh: mm: ss y GMT (Z), puede extraer los dígitos de cada miembro y compararlos como enteros

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

Si la fecha se puede formatear de manera diferente, es posible que deba agregar algo para las personas con problemas de 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
¿No podrías usarlo Date.parse?
Rocket Hazmat

1

Para completar, aquí hay una posible implementación genérica corta de sortBy:

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

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

Tenga en cuenta que esto utiliza el método de ordenación de matrices que se ordena en su lugar. para una copia, puede usar arr.concat () o arr.slice (0) o un método similar para crear una copia.


1

Con esto podemos pasar una función clave para usar para la clasificación

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;
    }); 
}

Entonces por ejemplo si tenemos

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

Podemos hacer

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

Puede usar la biblioteca de utilidades Lodash para resolver este problema (es una biblioteca bastante eficiente):

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>

Puede encontrar documentación aquí: https://lodash.com/docs/4.17.15#orderBy


0

Puede crear un cierre y pasarlo de esa manera aquí está mi ejemplo trabajando

$.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
  • Use Array.sort()para ordenar una matriz
  • Clonar matriz usando el operador spread ( ) para hacer que la función sea pura
  • Ordenar por clave deseada ( updated_at)
  • Convertir cadena de fecha a objeto de fecha
  • Array.sort() funciona restando dos propiedades del elemento actual y siguiente si es un número / objeto en el que puede realizar operaciones arrítmicas
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

Me enfrento a lo mismo, así que manejo esto con un por qué genérico y construyo una función para esto:

//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;
    })
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.