42 votes

Backbone.js collection comparateur de trier par plusieurs champs?

  this.col = Backbone.Collection.extend({
    model: M,
    comparator: function(item) {
      return item.get("level");
    }
  });

Ce code ci-dessus trie les éléments par niveau. Je veux trier par niveau, puis par titre. Puis-je le faire? Merci.

73voto

bjeanes Points 2835

@amchang87 réponse travaille certainement, mais un autre que j'ai trouvé du travail est simplement de retourner un tableau triable champs:

this.col = Backbone.Collection.extend({
    model: M,
    comparator: function(item) {
      return [item.get("level"), item.get("title")]
    }
});

Je n'ai pas testé ce dans plusieurs navigateurs mais comme je pense qu'il s'appuie sur de JS comportement dans l'ordre de tri pour les tableaux (en fonction de leur contenu). Cela fonctionne bien dans WebKit.

7voto

hyong Points 31

La concaténation de chaîne fonctionne correctement lorsque le tri de plusieurs champs dans l'ordre croissant, mais il ne fonctionne pas pour moi parce que 1) j'ai eu à l'appui de l'asc/desc par champ et 2) certains champs ont été de numéro de champ (c'est à dire, je veux de 10 à venir après 2 s'il est ascendant). Alors, ci-dessous est un comparateur de fonction que j'ai utilisé et travaillé sur OK pour mes besoins. Il suppose que l'épine dorsale de la collection a une variable assignée avec "sortConfig", qui est un tableau d'objets JSON avec le champ nom et l'ordre de tri de la direction. Par exemple,

{
    "sort" : [
        {
            "field": "strField",
            "order": "asc"
         },
         {
             "field": "numField",
             "order": "desc"
         },
         ...
     ]
}

Avec l'objet JSON ci-dessus assignées comme "sortConfig' à la collecte, à la fonction ci-dessous feront épine Dorsale de trier par strField dans l'ordre croissant d'abord, puis de trier par numField dans l'ordre décroissant, etc. Si aucun ordre de tri est spécifié, il trie croissant par défaut.

multiFieldComparator: function(one, another) {
    // 'this' here is Backbone Collection
    if (this.sortConfig) {
        for (var i = 0; i < this.sortConfig.length; i++) {
            if (one.get(this.sortConfig[i].field) > another.get(this.sortConfig[i].field)) {
                return ("desc" != this.sortConfig[i].order) ? 1 : -1;
            } else if (one.get(this.sortConfig[i].field) == another.get(this.sortConfig[i].field)) {
                // do nothing but let the loop move further for next layer comparison
            } else {
                return ("desc" != this.sortConfig[i].order) ? -1 : 1;
            }
        }
    }
    // if we exited out of loop without prematurely returning, the 2 items being
    // compared are identical in terms of sortConfig, so return 0
    // Or, if it didn't get into the if block due to no 'sortConfig', return 0
    // and let the original order not change.
    return 0;
}

2voto

Lawrence Points 26

Retourner un tableau n'est pas compatible si vous avez besoin de trier par ordre décroissant et certains croissant...

J'ai créé un petit ensemble de fonctions qui peuvent être utilisées pour renvoyer la comparaison pertinente entier en arrière de la Dorsale Comparateur de fonction:

épine dorsale de la collection de multisort

1voto

amchang87 Points 664

La chose principale est que l'épine Dorsale de trie par une seule valeur relative d'un élément à un autre. Il n'est donc pas directement possible de trier par deux fois en une seule collection, mais je voudrais essayer cette.

this.col = Backbone.Collection.extend({
    model: M,
    comparator: function(item) {
      // make sure this returns a string!
      return item.get("level") + item.get("title");
    }
});

Ce que cela va faire est de retourner une chaîne de caractères comme "1Cool", "1intitulé", "2newTitle" ... Javascript doit trier les chaînes par le numérique caractère d'abord, puis chaque personnage par la suite. Mais cela ne fonctionne que tant que vos niveaux ont la même quantité de chiffres. C'est à dire "001title" vs "200title". L'idée principale est que vous avez besoin pour produire deux objets comparables, ligne un nombre ou une chaîne de caractères, qui peuvent être comparés l'un à l'autre, fondée sur un seul critère.

Une autre solution serait d'utiliser le trait de soulignement de "groupe" de votre niveau, puis utilisez "sortby" pour trier manuellement chaque groupe de niveau, puis remplacer manuellement la collection sous-jacente à ce tableau nouvellement créé. Vous pouvez probablement l'installation d'une fonction pour faire cela à chaque fois que la collection "modifications".

0voto

useless Points 1083

"inspiré" dans hyong réponse.

Cela vous permet également de modifier les données avant de les comparer, valueTransforms est un objet, si il y a un attribut dans l'objet qui a une fonction, il sera utilisé.

    /*
     * @param {Object} sortOrders ie: 
     * {
     *     "description": "asc",
     *     "duedate": "desc",
     * }
     * @param {Object} valueTransforms
     */
    setMultiFieldComparator: function(sortOrders, valueTransforms) {
        var newSortOrders = {}, added = 0;
        _.each(sortOrders, function(sortOrder, sortField) {
            if (["asc", "desc"].indexOf(sortOrder) !== -1) {
                newSortOrders[sortField] = sortOrder;
                added += 1;
            }
        });
        if (added) {
            this.comparator = this._multiFieldComparator
                .bind(this, newSortOrders, valueTransforms || this.model.prototype.valueTransforms || {});
        } else {
            this.comparator = null;
        }
    },

    _multiFieldComparator: function(sortOrders, valueTransforms, one, another) {
        var retVal = 0;
        if (sortOrders) {
            _.every(sortOrders, function(sortOrder, sortField) {
                var oneValue = one.get(sortField),
                    anotherValue = another.get(sortField);
                if (valueTransforms[sortField] instanceof Function) {
                    oneValue = valueTransforms[sortField](oneValue);
                    anotherValue = valueTransforms[sortField](anotherValue);
                }
                if (oneValue > anotherValue) {
                    retVal = ("desc" !== sortOrder) ? 1 : -1;
                } else if (oneValue < anotherValue) {
                    retVal = ("desc" !== sortOrder) ? -1 : 1;
                } else {
                    //continue
                    return true;
                }
            });
        }
        return retVal;
    },

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X