Balik urutan urutan dengan Backbone.js

89

Dengan Backbone.js saya menyiapkan koleksi dengan fungsi komparator. Ini memilah model dengan baik, tetapi saya ingin membalik urutannya.

Bagaimana cara mengurutkan model dalam urutan turun daripada naik?

Drew Dara-Abrams
sumber
2
Untuk pengurutan terbalik pada String (termasuk bidang create_at), lihat jawaban ini di pertanyaan lain
Eric Hu
2
Ada dukungan untuk pembanding gaya-sortir sejak awal tahun 2012. Terima saja 2 argumen dan kembalikan -1, 0 atau 1. github.com/documentcloud/backbone/commit/…
geon
Inilah yang berhasil untuk saya (String dan Angka): stackoverflow.com/questions/5013819/…
Fasani

Jawaban:

133

Nah, Anda bisa mengembalikan nilai negatif dari pembanding. Jika kita ambil misalnya contoh dari situs Backbone dan ingin membalik urutannya, maka akan terlihat seperti ini:

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
sumber
3
hi5 untuk menggunakan kode contoh yang sama seperti milik saya!, dan dalam perbedaan 15 detik. Seperti yang harus saya katakan.
DhruvPathak
Oh, tentu saja, hanya tanda minus. Terima kasih atas tanggapan cepat Anda!
Drew Dara-Abrams
Perhatikan juga bahwa (seperti yang disebutkan di situs Backbone) ini rusak jika Anda mengubah salah satu atribut model (jika Anda menambahkan atribut "panjang" ke bab misalnya). Dalam hal ini, Anda harus memanggil "sort ()" pada Collection secara manual.
cmcculloh
9
Ada dukungan untuk pembanding gaya-sortir sejak awal 2012. Terima saja 2 argumen dan kembalikan -1, 0 atau 1. github.com/documentcloud/backbone/commit/…
geon
Tanggapan @Fasani berfungsi untuk jenis selain angka, silakan lihat: stackoverflow.com/questions/5013819/…
JayC
56

Secara pribadi, saya tidak begitu senang dengan solusi yang diberikan di sini:

  • Solusi mengalikan dengan -1 gagal berfungsi jika jenis pengurutannya non-numerik. Meskipun ada cara untuk mengatasi ini (dengan menelepon Date.getTime()misalnya) kasus-kasus ini bersifat spesifik. Saya ingin cara umum untuk membalik arah apa pun, tanpa harus khawatir tentang jenis bidang tertentu yang sedang diurutkan.

  • Untuk solusi string, membuat string satu karakter pada satu waktu tampak seperti hambatan kinerja, terutama saat menggunakan koleksi besar (atau memang, string bidang sortir besar)

Berikut solusi yang berfungsi dengan baik untuk bidang String, Tanggal dan Numerik:

Pertama, nyatakan pembanding yang akan membalikkan hasil dari fungsi sortBy, seperti ini:

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

Sekarang, jika Anda ingin membalik arah pengurutan, yang perlu kita lakukan adalah:

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

Alasan mengapa ini berfungsi adalah karena Backbone.Collection.sortberperilaku berbeda jika fungsi sortir memiliki dua argumen. Dalam hal ini, ia berperilaku dengan cara yang sama seperti pembanding yang diteruskan Array.sort. Solusi ini bekerja dengan mengadaptasi fungsi sortBy argumen tunggal menjadi komparator dua argumen dan membalikkan hasilnya.

Andrew Newdigate
sumber
7
Sebagai orang dengan jawaban paling positif untuk pertanyaan ini, saya sangat menyukai pendekatan ini dan menurut saya pendekatan ini harus mendapat lebih banyak suara positif.
Andrew De Andrade
Terima kasih banyak Andrew
Andrew Newdigate
Sangat bagus, saya akhirnya mengubah ini sedikit dan mengabstraksikannya dalam prototipe Backbone.Collection. Terima kasih banyak!
Kendrick Ledet
46

Pembanding koleksi Backbone.js bergantung pada metode Underscore.js _.sortBy. Cara sortBy diimplementasikan berakhir dengan "membungkus" javascript .sort () dengan cara yang membuat pengurutan string secara terbalik menjadi sulit. Negasi sederhana dari string akhirnya mengembalikan NaN dan memutus pengurutan.

Jika Anda perlu melakukan pengurutan terbalik dengan String, seperti urutan abjad terbalik, berikut cara yang benar-benar hack untuk melakukannya:

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

Ini tidak berarti cantik, tapi ini adalah "negator string". Jika Anda tidak ragu untuk mengubah jenis objek asli dalam javascript, Anda dapat membuat kode lebih jelas dengan mengekstrak negator string dan menambahkannya sebagai metode pada String.Prototype. Namun Anda mungkin hanya ingin melakukan ini jika Anda tahu apa yang Anda lakukan, karena memodifikasi jenis objek asli dalam javascript dapat menimbulkan konsekuensi yang tidak terduga.

Andrew De Andrade
sumber
Ini sangat berguna. Terima kasih Andrew!
Abel
1
Tidak masalah. Saya hanya akan menambahkan bahwa Anda harus bertanya pada diri sendiri mengapa Anda menerapkan urutan alfabet terbalik dan apakah mungkin ada solusi yang lebih elegan dari perspektif pengalaman pengguna.
Andrew De Andrade
1
@AndrewDeAndrade Tentang alasan: Saya ingin mengurutkan koleksi berdasarkan atribut 'create_at', menyajikan yang terbaru terlebih dahulu. Atribut ini adalah salah satu default Backbone, dan disimpan sebagai String. Jawaban Anda paling mendekati apa yang saya inginkan. Terima kasih!
Eric Hu
Ini luar biasa, kami telah mengerjakan solusi untuk ini selama satu jam terakhir sekarang.
28

Solusi saya adalah membalikkan hasil demi hasil.

Untuk mencegah rendering ganda, pertama urutkan dengan silent, lalu picu 'reset'.

collections.sort({silent:true})
collections.models = collections.models.reverse();
collections.trigger('reset', collections, {});
Tomasz Trela
sumber
2
Solusi yang lebih baik adalah meniadakan hasil pembanding.
Daniel X Moore
7
Daniel, itu tidak selalu memungkinkan, misalnya saat mengurutkan string atau Tanggal.
Gavin Schulz
1
Tentu itu. Untuk Tanggal Anda dapat memiliki komparator kembali -Date.getTime(), mungkin dengan beberapa peretasan tambahan jika Anda yakin tanggal di sistem Anda akan melewati zaman unix. Untuk alfabet, lihat jawaban Andrew di atas.
asparagino
@DanielXMoore Mengapa itu selalu menjadi solusi yang lebih baik? Apakah lebih cepat? Secara pribadi saya merasa sangat mudah untuk menyimpan atribut yang terakhir kali diurutkan oleh pengguna untuk koleksi tersebut, lalu membalikkan urutan saat ini jika dia mengurutkan lagi berdasarkan atribut itu; (untuk menerapkan perilaku pengurutan yang Anda lihat di tabel Wikipedia, di mana arah pengurutan dapat diubah dengan berulang kali mengklik atribut di header tabel) Dengan cara itu saya menjaga logika pembanding saya tetap bersih dan menyelamatkan diri dari operasi pengurutan jika kita beralih dari Asc untuk Desc sort order
Mansiemans
13

Ubah fungsi komparator Anda untuk mengembalikan beberapa nilai proporisional terbalik alih-alih mengembalikan data Anda saat ini. Beberapa kode dari: http://documentcloud.github.com/backbone/#Collection-comparator

Contoh:

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
sumber
9

Berikut ini bekerja dengan baik untuk saya:

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.dll
sumber
8

Saya membaca beberapa di mana fungsi komparator Backbone baik menggunakan atau meniru fungsi JavaScript Array.prototype.sort (). Artinya, model ini menggunakan 1, -1 atau 0 untuk menentukan urutan setiap model dalam koleksi. Ini terus diperbarui dan koleksi tetap dalam urutan yang benar berdasarkan pembanding.

Info tentang Array.prototype.sort () dapat ditemukan di sini: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort?redirectlocale=en-US&redirectslug=JavaScript% 2FReferensi% 2FGlobal_Objects% 2FArray% 2Fsort

Banyak jawaban atas pertanyaan ini hanya membalik urutan sebelum membukanya ke halaman. Ini tidak ideal.

Menggunakan artikel di atas di Mozilla sebagai panduan, saya dapat menjaga koleksi saya diurutkan dalam urutan terbalik menggunakan kode berikut, kemudian kami cukup merender koleksi ke halaman dalam urutan saat ini dan kami dapat menggunakan bendera (reverseSortDirection) untuk membalik urutan .

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

},

Pembanding, kecuali dua model, pada perubahan pada koleksi atau model, fungsi kompartor berjalan dan mengubah urutan koleksi.

Saya telah menguji pendekatan ini dengan Bilangan dan String dan tampaknya berhasil dengan sempurna untuk saya.

Saya sangat berharap jawaban ini dapat membantu karena saya telah berjuang dengan masalah ini berkali-kali dan biasanya berakhir dengan penyelesaian.

Fasani
sumber
1
Saya suka ini karena logikanya ada dalam koleksi, tetapi Anda dapat dengan mudah mengatur sortKeydan reverseSortDirectiondan memanggil .sort()dari tampilan mana pun yang memiliki pegangan pada koleksi. Bagi saya, lebih sederhana daripada jawaban dengan suara terbanyak lainnya.
Noah Heldman
5

Ini dapat dilakukan secara elegan dengan mengganti metode sortBy. Berikut ini contohnya

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

});

Jadi Anda bisa menggunakannya seperti ini:

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

Solusi ini tidak bergantung pada jenis bidang.

Aman Mahajan
sumber
1
Bagus! "void 0" dapat diganti dengan "undefined".
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
sumber
2

Berikut adalah solusi yang sangat sederhana, bagi mereka yang hanya ingin membalik pesanan saat ini. Ini berguna jika Anda memiliki tabel yang kolomnya dapat diurutkan dalam dua arah.

collection.models = collection.models.reverse()

Anda dapat menggabungkannya dengan sesuatu seperti ini jika Anda tertarik dengan kasus tabel (coffeescript):

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

  collection.sort()

  if reverseOrder
    collection.models = collection.models.reverse()
dezman
sumber
2
Dalam kasus ini, ketika model baru ditambahkan ke koleksi, itu tidak akan diurutkan dengan benar karena metode .set menyimpan model baru yang ditambahkan secara berurutan dengan menggunakan metode sortir, dapatkan hasil dari sebelum reverseOrder diterapkan.
Fabio
1

Untuk urutan terbalik pada id:

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

Saya memiliki persyaratan yang sedikit berbeda, yaitu saya menampilkan model saya dalam tabel, dan saya ingin dapat mengurutkannya berdasarkan kolom apa pun (properti model) dan menaik atau menurun.

Diperlukan waktu yang cukup lama untuk menyelesaikan semua kasus (di mana nilai dapat berupa string, string kosong, tanggal, angka. Namun saya pikir ini cukup dekat.

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

menggunakan:

    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
sumber
0

Saya baru saja mengesampingkan fungsi sortir untuk membalikkan larik model setelah selesai. Juga saya menahan memicu acara 'sort' sampai setelah kebalikannya selesai.

    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;
    },
pengguna2686693
sumber
0

Baru-baru ini mengalami masalah ini dan baru saja memutuskan untuk menambahkan metode rsort.

    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;

            }
    });

Mudah-mudahan seseorang akan merasakan manfaatnya

Diego Alejos
sumber
0

Berikut adalah cara cepat dan mudah untuk menyortir ulang model koleksi menggunakan UnderscoreJS

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

John Ryan Cottam
sumber