Sortierreihenfolge mit Backbone.js umkehren

89

Mit Backbone.js habe ich eine Sammlung mit einer Komparatorfunktion eingerichtet. Es ist schön, die Modelle zu sortieren, aber ich möchte die Reihenfolge umkehren.

Wie kann ich die Modelle in absteigender Reihenfolge sortieren, anstatt aufsteigend?

Drew Dara-Abrams
quelle
2
Informationen zur umgekehrten Sortierung von Zeichenfolgen (einschließlich der Felder created_at) finden Sie in dieser Antwort auf eine andere Frage
Eric Hu,
2
Seit Anfang 2012 werden Komparatoren im Sortierstil unterstützt. Akzeptieren Sie einfach 2 Argumente und geben Sie -1, 0 oder 1 zurück. Github.com/documentcloud/backbone/commit/…
geon
Dies ist, was für mich funktioniert hat (Strings and Numbers): stackoverflow.com/questions/5013819/…
FMD

Antworten:

133

Nun, Sie können negative Werte vom Komparator zurückgeben. Wenn wir zum Beispiel das Beispiel von der Backbone-Site nehmen und die Reihenfolge umkehren möchten, sieht es folgendermaßen aus:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

chapters.comparator = function(chapter) {
  return -chapter.get("page"); // Note the minus!
};

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));

alert(chapters.pluck('title'));
Infeligo
quelle
3
hi5 für die Verwendung des gleichen Beispielcodes wie meiner! und innerhalb von 15 Sekunden Unterschied. Gleichgesinnt muss ich sagen.
DhruvPathak
Oh, natürlich nur ein Minuszeichen. Vielen Dank für Ihre schnellen Antworten!
Drew Dara-Abrams
Beachten Sie auch, dass dies (wie auf der Backbone-Website erwähnt) nicht funktioniert, wenn Sie eines der Modellattribute ändern (wenn Sie beispielsweise dem Kapitel ein "Längen" -Attribut hinzufügen). In diesem Fall müssen Sie "sort ()" für die Sammlung manuell aufrufen.
cmcculloh
9
Seit Anfang 2012 werden Komparatoren im Sortierstil unterstützt. Akzeptieren Sie einfach 2 Argumente und geben Sie -1, 0 oder 1 zurück. Github.com/documentcloud/backbone/commit/…
geon
@ Fasani Antwort funktioniert für andere Typen als Zahlen, bitte schauen Sie: stackoverflow.com/questions/5013819/…
JayC
56

Persönlich bin ich mit keiner der hier angegebenen Lösungen so zufrieden:

  • Die Lösung zum Multiplizieren mit -1 funktioniert nicht, wenn der Sortiertyp nicht numerisch ist. Obwohl es Möglichkeiten gibt, dies zu umgehen (indem Sie Date.getTime()beispielsweise anrufen ), sind diese Fälle spezifisch. Ich möchte einen allgemeinen Weg, um die Richtung jeglicher Art umzukehren, ohne mir Gedanken über den spezifischen Typ des zu sortierenden Feldes machen zu müssen.

  • Für die String-Lösung scheint das Erstellen eines Strings zeichenweise ein Leistungsengpass zu sein, insbesondere bei Verwendung großer Sammlungen (oder großer Sortierfeld-Strings).

Hier ist eine Lösung, die für String-, Datums- und numerische Felder gut funktioniert:

Deklarieren Sie zunächst einen Komparator, der das Ergebnis eines Ergebnisses einer sortBy-Funktion wie folgt umkehrt:

function reverseSortBy(sortByFunction) {
  return function(left, right) {
    var l = sortByFunction(left);
    var r = sortByFunction(right);

    if (l === void 0) return -1;
    if (r === void 0) return 1;

    return l < r ? 1 : l > r ? -1 : 0;
  };
}

Wenn Sie nun die Richtung der Sortierung umkehren möchten, müssen wir nur noch Folgendes tun:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

// Your normal sortBy function
chapters.comparator = function(chapter) {
  return chapter.get("title"); 
};


// If you want to reverse the direction of the sort, apply 
// the reverseSortBy function.
// Assuming the reverse flag is kept in a boolean var called reverseDirection 
if(reverseDirection) {
   chapters.comparator = reverseSortBy(chapters.comparator);
}

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));

alert(chapters.pluck('title'));

Der Grund dafür ist, dass Backbone.Collection.sortsich das Verhalten anders verhält, wenn die Sortierfunktion zwei Argumente hat. In diesem Fall verhält es sich genauso wie der Komparator Array.sort. Diese Lösung funktioniert, indem die Funktion sortBy für einzelne Argumente in einen Komparator mit zwei Argumenten umgewandelt und das Ergebnis umgekehrt wird.

Andrew Newdigate
quelle
7
Als die Person mit der am besten bewerteten Antwort auf diese Frage mag ich diesen Ansatz sehr und denke, dass er mehr positive Stimmen haben sollte.
Andrew De Andrade
Vielen Dank Andrew
Andrew Newdigate
Sehr schön, am Ende habe ich dies ein wenig optimiert und im Prototyp von Backbone.Collection abstrahiert. Vielen Dank!
Kendrick Ledet
46

Der Sammlungskomparator von Backbone.js basiert auf der Underscore.js-Methode _.sortBy. Die Art und Weise, wie sortBy implementiert wird, führt dazu, dass Javascript .sort () so "eingepackt" wird, dass das umgekehrte Sortieren von Zeichenfolgen schwierig wird. Durch einfaches Negieren des Strings wird NaN zurückgegeben und die Sortierung unterbrochen.

Wenn Sie eine umgekehrte Sortierung mit Strings durchführen müssen, z. B. eine umgekehrte alphabetische Sortierung, ist dies eine wirklich hackige Methode:

comparator: function (Model) {
  var str = Model.get("name");
  str = str.toLowerCase();
  str = str.split("");
  str = _.map(str, function(letter) { 
    return String.fromCharCode(-(letter.charCodeAt(0)));
  });
  return str;
}

Es ist keineswegs hübsch, aber es ist ein "String Negator". Wenn Sie keine Bedenken haben, native Objekttypen in Javascript zu ändern, können Sie den Code klarer gestalten, indem Sie den String-Negator extrahieren und als Methode für String.Prototype hinzufügen. Sie möchten dies jedoch wahrscheinlich nur tun, wenn Sie wissen, was Sie tun, da das Ändern nativer Objekttypen in Javascript unvorhergesehene Folgen haben kann.

Andrew De Andrade
quelle
Das ist sehr praktisch. Danke Andrew!
Abel
1
Kein Problem. Ich möchte nur hinzufügen, dass Sie sich fragen sollten, warum Sie eine umgekehrte alphabetische Sortierung implementieren und ob es aus Sicht der Benutzererfahrung möglicherweise eine elegantere Lösung gibt.
Andrew De Andrade
1
@AndrewDeAndrade Warum: Ich möchte eine Sammlung nach dem Attribut 'created_at' sortieren und zuerst die neuesten bereitstellen. Dieses Attribut ist eines der Standardattribute von Backbone und wird als Zeichenfolge gespeichert. Ihre Antwort kommt dem, was ich will, am nächsten. Vielen Dank!
Eric Hu
Das ist erstaunlich, ich arbeite jetzt seit einer Stunde an einer Lösung dafür.
28

Meine Lösung bestand darin, die Ergebnisse nach dem Sortieren umzukehren.

Um doppeltes Rendern zu verhindern, sortieren Sie zuerst mit Silent und lösen Sie dann 'Reset' aus.

collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
Tomasz Trela
quelle
2
Eine bessere Lösung besteht darin, das Ergebnis des Komparators zu negieren.
Daniel X Moore
7
Daniel, das ist nicht immer möglich, zum Beispiel beim Sortieren von Strings oder Dates.
Gavin Schulz
1
Sicher ist es das. Für Date können Sie den Komparator zurückgeben lassen -Date.getTime(), möglicherweise mit etwas mehr Hacking, wenn Sie glauben, dass Dates in Ihrem System die Unix-Epoche überschreiten werden. In alphabetischer Reihenfolge sehen Sie sich Andrews Antwort oben an.
Asparagino
@ DanielXMoore Warum ist das unbedingt eine bessere Lösung? Ist es schneller Persönlich finde ich es sehr einfach, das Attribut zu speichern, nach dem der Benutzer die Sammlung zuletzt sortiert hat, und dann die aktuelle Sortierung umzukehren, wenn er erneut nach diesem Attribut sortiert. (Um das Sortierverhalten zu implementieren, das Sie in einer Wikipedia-Tabelle sehen, in der die Sortierrichtung durch wiederholtes Klicken auf das Attribut in der Tabellenüberschrift umgeschaltet werden kann.) Auf diese Weise halte ich meine Komparatorlogik sauber und spare mir eine Sortieroperation, wenn wir von Asc wechseln nach Desc Sortierreihenfolge
Mansiemans
13

Ändern Sie Ihre Komparatorfunktion, um einen umgekehrten Proportionalwert zurückzugeben, anstatt die Daten zurückzugeben, die Sie gerade sind. Einige Codes von: http://documentcloud.github.com/backbone/#Collection-comparator

Beispiel:

var Chapter  = Backbone.Model;
var chapters = new Backbone.Collection;

/* Method 1: This sorts by page number */
chapters.comparator = function(chapter) {
  return chapter.get("page");
};

/* Method 2: This sorts by page number in reverse */
chapters.comparator = function(chapter) {
  return -chapter.get("page");
};

chapters.add(new Chapter({page: 9, title: "The End"}));
chapters.add(new Chapter({page: 5, title: "The Middle"}));
chapters.add(new Chapter({page: 1, title: "The Beginning"}));
DhruvPathak
quelle
9

Folgendes hat bei mir gut funktioniert:

comparator: function(a, b) {
  // Optional call if you want case insensitive
  name1 = a.get('name').toLowerCase();
  name2 = b.get('name').toLowerCase();

  if name1 < name2
    ret = -1;
  else if name1 > name2
    ret = 1;
  else
    ret = 0;

  if this.sort_dir === "desc"
    ret = -ret
  return ret;
}

collection.sort_dir = "asc";
collection.sort(); // returns collection in ascending order
collection.sort_dir = "desc";
collection.sort(); // returns collection in descending order
rbhitchcock
quelle
8

Ich habe einige gelesen, in denen die Backbone-Komparatorfunktion die JavaScript-Funktion Array.prototype.sort () verwendet oder nachahmt. Dies bedeutet, dass entweder 1, -1 oder 0 verwendet wird, um die Sortierreihenfolge für jedes Modell in der Sammlung festzulegen. Dies wird ständig aktualisiert und die Sammlung bleibt in der richtigen Reihenfolge basierend auf dem Komparator.

Informationen zu Array.prototype.sort () finden Sie hier: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=de-US&redirectslug=JavaScript% 2FReference% 2FGlobal_Objects% 2FArray% 2Fsort

Viele Antworten auf diese Frage kehren einfach die Reihenfolge um, bevor sie auf die Seite gerendert werden. Das ist nicht ideal.

Mit dem obigen Artikel über Mozilla als Leitfaden konnte ich meine Sammlung mit dem folgenden Code in umgekehrter Reihenfolge sortieren. Dann rendern wir die Sammlung einfach in der aktuellen Reihenfolge auf Seite und können ein Flag (reverseSortDirection) zum Umkehren der Reihenfolge verwenden .

//Assuming this or similar will be in your Backbone.Collection.
sortKey: 'id', //The field name of the item in your collection

reverseSortDirection: false,

comparator: function(a, b) {

  var sampleDataA = a.get(this.sortKey),
      sampleDataB = b.get(this.sortKey);

      if (this.reverseSortDirection) {
        if (sampleDataA > sampleDataB) { return -1; }
        if (sampleDataB > sampleDataA) { return 1; }
        return 0;
      } else {
        if (sampleDataA < sampleDataB) { return -1; }
        if (sampleDataB < sampleDataA) { return 1; }
        return 0;
      }

},

Mit Ausnahme der beiden Modelle von Comparator wird bei Änderungen an der Sammlung oder dem Modell die Kompartimentfunktion ausgeführt und die Reihenfolge der Sammlung geändert.

Ich habe diesen Ansatz mit Numbers and Strings getestet und er scheint perfekt für mich zu funktionieren.

Ich hoffe wirklich, dass diese Antwort helfen kann, da ich viele Male mit diesem Problem zu kämpfen habe und normalerweise eine Problemumgehung verwende.

MKS
quelle
1
Ich mag das , weil die Logik lebt in der Sammlung, aber man kann einfach eingestellt sortKeyund reverseSortDirectionund Anruf .sort()von einem beliebigen Ansicht , die einen Griff an der Sammlung. Für mich einfacher als andere Antworten mit höheren Stimmen.
Noah Heldman
5

Dies kann elegant erfolgen, indem die sortBy-Methode überschrieben wird. Hier ist ein Beispiel

var SortedCollection = Backbone.Collection.extend({

   initialize: function () {
       // Default sort field and direction
       this.sortField = "name";
       this.sortDirection = "ASC";
   },

   setSortField: function (field, direction) {
       this.sortField = field;
       this.sortDirection = direction;
   },

   comparator: function (m) {
       return m.get(this.sortField);
   },

   // Overriding sortBy (copied from underscore and just swapping left and right for reverse sort)
   sortBy: function (iterator, context) {
       var obj = this.models,
           direction = this.sortDirection;

       return _.pluck(_.map(obj, function (value, index, list) {
           return {
               value: value,
               index: index,
               criteria: iterator.call(context, value, index, list)
           };
       }).sort(function (left, right) {
           // swap a and b for reverse sort
           var a = direction === "ASC" ? left.criteria : right.criteria,
               b = direction === "ASC" ? right.criteria : left.criteria;

           if (a !== b) {
               if (a > b || a === void 0) return 1;
               if (a < b || b === void 0) return -1;
           }
           return left.index < right.index ? -1 : 1;
       }), 'value');
   }

});

Sie können es also folgendermaßen verwenden:

var collection = new SortedCollection([
  { name: "Ida", age: 26 },
  { name: "Tim", age: 5 },
  { name: "Rob", age: 55 }
]);

//sort by "age" asc
collection.setSortField("age", "ASC");
collection.sort();

//sort by "age" desc
collection.setSortField("age", "DESC");
collection.sort();

Diese Lösung hängt nicht vom Feldtyp ab.

Aman Mahajan
quelle
1
Gut! "void 0" kann durch "undefined" ersetzt werden.
GMO
3
// For numbers, dates, and other number-like things
var things = new Backbone.Collection;
things.comparator = function (a, b) { return b - a };

// For strings
things.comparator = function (a, b) {
  if (a === b) return 0;
  return a < b ? -1 : 1;
};
wprl
quelle
2

Hier ist eine wirklich einfache Lösung für diejenigen, die einfach die aktuelle Reihenfolge umdrehen möchten. Dies ist nützlich, wenn Sie eine Tabelle haben, deren Spalten in zwei Richtungen angeordnet werden können.

collection.models = collection.models.reverse()

Sie können es mit so etwas kombinieren, wenn Sie an der Tischhülle (Coffeescript) interessiert sind:

  collection.comparator = (model) ->
    model.get(sortByType)

  collection.sort()

  if reverseOrder
    collection.models = collection.models.reverse()
dezman
quelle
2
In diesem Fall wird ein neues Modell, das der Auflistung hinzugefügt wird, nicht korrekt sortiert, da die .set-Methode die neu hinzugefügten Modelle mithilfe der Sortiermethode in der richtigen Reihenfolge hält und das Ergebnis abruft, bevor der reverseOrder angewendet wird.
Fabio
1

Für umgekehrte Reihenfolge auf ID:

comparator: function(object) { return (this.length - object.id) + 1; }
mzzl
quelle
0

Ich hatte eine etwas andere Anforderung, da ich meine Modelle in einer Tabelle anzeigte und sie nach jeder Spalte (Modelleigenschaft) und entweder aufsteigend oder absteigend sortieren wollte.

Es hat eine Menge Zeit gekostet, um alle Fälle zum Laufen zu bringen (wobei Werte Zeichenfolgen, leere Zeichenfolgen, Datumsangaben, Zahlen sein können. Ich denke jedoch, dass dies ziemlich nahe ist.

    inverseString: function(str)
    {
        return str.split("").map(function (letter) { return String.fromCharCode(-(letter.charCodeAt(0))); }).join("");
    }
    getComparisonValue: function (val, desc)
    {
        var v = 0;
        // Is a string
        if (typeof val === "string")
        {
            // Is an empty string, upgrade to a space to avoid strange ordering
            if(val.length === 0)
            {
                val = " ";
                return desc ? this.inverseString(val) : val;
            }                

            // Is a (string) representing a number
            v = Number(val);
            if (!isNaN(v))
            {
                return desc ? -1 * v : v;
            }
            // Is a (string) representing a date
            v = Date.parse(val);
            if (!isNaN(v))
            {
                return desc ? -1 * v : v;
            }
            // Is just a string
            return desc ? this.inverseString(val) : val;
        }
        // Not a string
        else
        {
            return desc ? -1 * val : val;
        }
    },

verwenden:

    comparator: function (item)
    {
        // Store the collumn to 'sortby' in my global model
        var property = app.collections.global.get("sortby");

        // Store the direction of the sorting also in the global model
        var desc = app.collections.global.get("direction") === "DESC";

        // perform a comparison based on property & direction
        return this.getComparisonValue(item.get(property), desc);
    },
Josh Mc
quelle
0

Ich habe gerade die Sortierfunktion überschrieben, um das Modellarray nach Abschluss umzukehren. Außerdem habe ich das Auslösen des Sortierereignisses erst nach Abschluss des Rückwärtsgangs unterbrochen.

    sort : function(options){

        options = options || {};

        Backbone.Collection.prototype.sort.call(this, {silent:true});
        this.models.reverse();

        if (!options.silent){
            this.trigger('sort', this, options);
        }

        return this;
    },
user2686693
quelle
0

Ich bin kürzlich auf dieses Problem gestoßen und habe gerade beschlossen, eine rsort-Methode hinzuzufügen.

    Collection = Backbone.Collection.extend({
            comparator:function(a, b){
                return a>b;
            },
            rsort:function(){
                var comparator = this.comparator;

                this.comparator = function(){
                    return -comparator.apply(this, arguments);
                }
                this.sort();

                this.comparator = comparator;

            }
    });

Hoffentlich findet das jemand nützlich

Diego Alejos
quelle
0

Hier ist eine schnelle und einfache Möglichkeit, die Modelle einer Sammlung mithilfe von UnderscoreJS rückwärts zu sortieren

 var reverseCollection = _.sortBy(this.models, function(model) {
   return self.indexOf(model) * -1;
 });

John Ryan Cottam
quelle