Trier le tableau d'objets par clé unique avec valeur de date

257

J'ai un tableau d'objets avec plusieurs paires de valeurs clés, et je dois les trier en fonction de '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"
    }
]

Quelle est la manière la plus efficace de le faire?

monstre cookie
la source
@Topener Ce lien ressemble à une question sur PHP
David Brainer
mon erreur .. ne l'a pas lu correctement
Rene Pot

Réponses:

339

Vous pouvez utiliser Array.sort.

Voici un exemple:

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

Rocket Hazmat
la source
17
Ne pourriez-vous pas utiliser keyA - keyB(ou éventuellement keyB - keyA)? Les objets de date ont une valueOf()méthode.
soktinpk
retourner a. updated_at <b. updated_at? 1: -1 fonctionne pour moi. Pas besoin d'analyser dans un format de date.
oliversisson
La possibilité de faire juste a-best vraiment importante si vous voulez éviter le code long. Bien que le code long ne soit pas nécessairement mauvais dans ce cas, je pense que la verbosité le rend plus difficile à comprendre. J'utilise actuellement values.sort((a,b)=>a.attr-b.attr). Devoir écrire 5 lignes chaque fois que vous devez trier un tableau devient fastidieux.
AnnanFay
Cet exemple n'est pas efficace et fait beaucoup de calculs inutiles. Le tri peut prendre n'importe quel nombre positif ou négatif comme résultat valide. Vos calculs supplémentaires pour le forcer à 1,0, -1 ne sont pas nécessaires et ajoutent simplement des calculs supplémentaires par exécution.
Patrick W. McMahon
Fonctionne très bien avec vous
Andy
158

J'ai déjà répondu à une question vraiment similaire ici: Fonction simple pour trier un tableau d'objets

Pour cette question, j'ai créé cette petite fonction qui pourrait faire ce que vous voulez:

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));
    });
}
David Brainer
la source
11
Vous, monsieur, êtes l'homme. blogs.citypages.com/blotter/assets_c/2009/02/…
Dom
2
Comment inverseriez-vous cela?
4
Pour le rendre insensible à la casse, vous pouvez ajouter .toLowerCase () aux variables x et y
Jacob van Lingen
4
Pour inverser des fonctions de tri comme celles-ci, multipliez simplement le résultat par -1:)
Svish
5
Ou prenez simplement la sortie et utilisez la array = array.reverse()fonction.
Luke Stevenson
31

La méthode Array.sort () trie les éléments d'un tableau en place et retourne le tableau. Soyez prudent avec Array.sort () car il n'est pas immuable . Pour le tri immuable, utilisez immuable-sort .

Cette méthode consiste à trier le tableau en utilisant votre courant updated_atau format ISO. Nous utilisons new Data(iso_string).getTime()pour convertir le temps ISO en horodatage Unix. Un horodatage Unix est un nombre sur lequel nous pouvons faire des calculs simples. Nous soustrayons le premier et le deuxième horodatage du résultat; si le premier horodatage est plus grand que le second, le numéro de retour sera positif. Si le deuxième nombre est plus grand que le premier, la valeur de retour sera négative. Si les deux sont identiques, le retour sera nul. Cela correspond parfaitement aux valeurs de retour requises pour la fonction inline.

Pour ES6 :

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

Pour ES5 :

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

Si vous modifiez vos updated_athorodatages Unix, vous pouvez le faire:

Pour ES6 :

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

Pour ES5 :

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

Au moment de cette publication, les navigateurs modernes ne prennent pas en charge ES6. Pour utiliser ES6 dans les navigateurs modernes, utilisez babel pour transpiler le code en ES5. Attendez-vous à la prise en charge du navigateur pour ES6 dans un avenir proche.

Array.sort () devrait récupérer une valeur de retour de l'un des 3 résultats possibles:

  • Un nombre positif (premier élément> deuxième élément)
  • Un nombre négatif (premier élément <deuxième élément)
  • 0 si les deux éléments sont égaux

Notez que la valeur de retour sur la fonction en ligne peut être n'importe quel nombre positif ou négatif. Array.Sort () ne se soucie pas du numéro de retour. Il ne se soucie que si la valeur de retour est positive, négative ou nulle.

Pour le tri immuable: (exemple dans ES6)

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

Vous pouvez également l'écrire de cette façon:

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

L'import-from you see est une nouvelle façon d'inclure javascript dans ES6 et rend votre code très propre. Mon préféré.

Le tri immuable ne mute pas le tableau source mais renvoie un nouveau tableau. L'utilisation constest recommandée sur les données immuables.

Patrick W. McMahon
la source
19

Voici une version légèrement modifiée de la réponse @David Brainer-Bankers qui trie alphabétiquement par chaîne ou numériquement par numéro, et garantit que les mots commençant par des majuscules ne trient pas les mots commençant par une minuscule (par exemple "pomme, précoce" serait affiché dans cet ordre).

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));
    });
}
Brad Parks
la source
3
La solution proposée peut donner une erreur si a [clé] et b [clé] ne sont pas les deux chaînes. Je suggère de remplacer y = y.toLowerCase () par y = ("" + y) .toLowerCase ()
user8074
sort peut accepter n'importe quel nombre positif ou négatif comme retour valide. Vous êtes des calculs supplémentaires pour le forcer à 1,0, -1 n'est pas nécessaire. Vous avez compliqué une valeur de retour simple. Il est préférable de ne pas ajouter de calculs supplémentaires qui ne font rien.
Patrick W. McMahon
15

Utilisez le j de soulignement ou le 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() renvoie un nouveau tableau

consultez http://underscorejs.org/#sortBy et lodash docs https://lodash.com/docs#sortBy

Mohammed Safeer
la source
6

Avec le support ES2015, cela peut être fait par:

foo.sort((a, b) => a.updated_at < b.updated_at ? -1 : 1)
savoir
la source
1
pas besoin de l'inline si remplacez le <par - et supprimez '? -1: 1 "vous obtiendrez un retour valide. Cet exemple déplace des éléments qui peuvent être égaux et pourraient donc donner des résultats inattendus. Pour des éléments égaux, un 0 doit être renvoyé.
Patrick W. McMahon
Merci d'avoir expliqué
knowbody
si updated_at est une heure ISO, cela ne fonctionnera pas. Cet exemple suppose des horodatages Unix mais l'OP a publié des données au format ISO. Il vous faudrait donc convertir les horodatages Unix pour faire une comparaison. Cela peut être fait avec new Date(iso_str).getTime()cela retournera un horodatage Unix.
Patrick W. McMahon
5

Données importées

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

POUR ordre croissant

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

Exemple pour l'ordre 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"
    }
]

POUR ordre décroissant

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

Exemple d'ordre 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"
    }
]
Anandan K
la source
3

Comme l'indique cette réponse, vous pouvez utiliser 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);

Ishpreet
la source
2

Juste une autre façon, plus mathématique , de faire la même chose mais plus court :

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

ou dans le style de flèche lambda lisse:

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

Cette méthode peut être effectuée avec n'importe quelle entrée numérique

Danny Mor
la source
réponse sous
estimée
2

J'ai créé une fonction de tri dans Typescript que nous pouvons utiliser pour rechercher des chaînes, des dates et des nombres dans un tableau d'objets. Il peut également trier sur plusieurs champs.

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

Usage:

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

arrayOfObjects.sort(CustomSorting.sortArrayOfObjects(sortOptions));
Ansuman
la source
1

Le tri par date au format ISO peut être coûteux, sauf si vous limitez les clients aux navigateurs les plus récents et les meilleurs, qui peuvent créer l'horodatage correct en analysant la chaîne par date.

Si vous êtes sûr de votre saisie et que vous savez que ce sera toujours aaaa-mm-jjThh: mm: ss et GMT (Z), vous pouvez extraire les chiffres de chaque membre et les comparer comme des nombres entiers

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

Si la date peut être formatée différemment, vous devrez peut-être ajouter quelque chose pour les personnes ayant un défi 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;
        }
    }
}
}
kennebec
la source
2
Ne pourriez-vous pas simplement utiliser Date.parse?
Rocket Hazmat
1

Pour être complet, voici une implémentation générique courte possible de sortBy:

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

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

Notez que cela utilise la méthode de tri des tableaux qui trie sur place. pour une copie, vous pouvez utiliser arr.concat () ou arr.slice (0) ou une méthode similaire pour créer une copie.

Légion
la source
1

Avec cela, nous pouvons passer une fonction clé à utiliser pour le tri

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

Ensuite, par exemple, si nous avons

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

Nous pouvons faire

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

ou

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

ou

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
*/
aljgom
la source
1

Vous pouvez utiliser la bibliothèque d'utilitaires Lodash pour résoudre ce problème (c'est une bibliothèque assez efficace):

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>

Vous pouvez trouver de la documentation ici: https://lodash.com/docs/4.17.15#orderBy

chétan
la source
0

Vous pouvez créer une fermeture et la passer de cette façon voici mon exemple de travail

$.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));
});
Michael Guild
la source
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);
Prasad
la source
0
  • Utilisez Array.sort()pour trier un tableau
  • Cloner un tableau à l'aide de l' opérateur d'étalement ( ) pour rendre la fonction pure
  • Trier par clé souhaitée ( updated_at)
  • Convertir une chaîne de date en objet date
  • Array.sort() fonctionne en soustrayant deux propriétés de l'élément actuel et suivant s'il s'agit d'un nombre / objet sur lequel vous pouvez effectuer des opérations arythmiques
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' } ]
*/
marcobiedermann
la source
0

Je fais face à la même chose, donc je gère cela avec un pourquoi générique et je crée une fonction pour cela:

//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;
    })
}
Idan
la source