2012-01-19 4 views
40
this.col = Backbone.Collection.extend({ 
    model: M, 
    comparator: function(item) { 
     return item.get("level"); 
    } 
    }); 

Этот выше код сортирует элементы по уровню. Я хочу сортировать по уровню, затем по названию. Я могу это сделать? Благодарю.Сортировщик коллекции Backbone.js сортирует по нескольким полям?

+3

С начала 2012 года поддерживается поддержка сортировочных компараторов. Просто примите 2 аргумента и верните -1, 0 или 1. https: // github.com/documentcloud/backbone/commit/6b3ff7b0359510917d9ead8c8e16a7457eef05ef – geon

ответ

72

@ ответ amchang87 определенно работает, а другой, что я нашел обработанный просто возвращая массив сортируемых полей:

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

Я не проверял это в нескольких браузерах еще, как я думаю, что он опирается на JS 'поведение в порядке сортировки для массивов (в зависимости от их содержимого). Он определенно работает в WebKit.

+5

С начала 2012 года существует поддержка сортировщиков стиля. Просто примите 2 аргумента и верните -1, 0 или 1. https://github.com/documentcloud/backbone/commit/6b3ff7b0359510917d9ead8c8e16a7457eef05ef – geon

+2

Так прямолинейно, это технология есть. Вот пример в CoffeeScript: https://gist.github.com/jhabdas/9822535 –

+5

Обратите внимание, что (по крайней мере, в Webkit) два поля будут сравниваться как строки. Если вы пытаетесь выполнить сортировку по номерам, у вас будет 1, 10, 2, 20, 200 и т. Д. – RobW

1

Главное, что Backbone сортируется по одному относительному значению одного элемента другому. Поэтому сразу невозможно сортировать дважды в одной коллекции, но я бы попробовал это.

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

Что это будет сделать, это вернуть строку как «1Cool», «1title», «2newTitle» ... Javascript должен сортировать строки по численному символу первых, то каждый символ впоследствии. Но это будет работать только до тех пор, пока ваши уровни будут иметь одинаковое количество цифр. IE "001title" против "200title". Основная идея состоит в том, что вам нужно создать два сопоставимых объекта, выстроить число или строку, которые можно сравнить друг с другом на основе одного критерия.

Другим решением было бы использовать подчеркивание для «группового» уровня, а затем использовать «sortby» для ручной сортировки каждой группы уровней, а затем вручную заменить базовую коллекцию на этот вновь созданный массив. Возможно, вы можете настроить функцию, чтобы сделать это всякий раз, когда коллекция «изменяет».

+0

Если я понимаю метод «groupby», который испортил бы мое моделирование данных, не так ли? Первое решение выглядит любезно, поэтому, спасибо за это. – Harry

+0

Не уверен, что это лучший способ сделать это. Например, если у вас было 2 модели: model1-> level: 1, title: "1-thing"; model2 -> level: 10, title: «-thing», тогда вы ожидаете, что он будет отсортирован model1, model2 - но этим компаратором будет сравниваться строка «11-вещь» и «10-вещь», так что сортировать в неправильном порядке –

+0

Это функционально идентично попыткам сортировать по массиву, возвращая '[1, 'x']' из одной функции сравнения аргументов сортирует по строке '" 1, x "' вместо сравнения элементы. –

7

Строка конкатенации прекрасно работает при сортировке нескольких полей в порядке возрастания, но это не сработало для меня, потому что 1) мне пришлось поддерживать asc/desc на поле и 2) определенные поля были полем числа (то есть, я хочу 10 приходить после 2, если он восходит). Итак, ниже была функция компаратора, которую я использовал и работал нормально для моих нужд. Предполагается, что у базовой системы есть переменная, назначенная с помощью 'sortConfig', которая представляет собой массив объектов JSON с именем поля и порядком порядка сортировки. Например,

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

С объекта JSON выше назначен «sortConfig» в коллекции, функция ниже будет делать Backbone сортировать по strField в порядке возрастания, а затем сортировать по numField в порядке убывания, и т.д. Если нет порядок сортировки указан, сортировка по умолчанию возрастает.

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

Возвращение массива не соответствует, если вам нужно отсортировать по убыванию и некоторые восходящие ...

Я создал небольшой набор функций, которые можно использовать для возврата соответствующего сравнения число обратно Backbone Компаратор функция:

backbone-collection-multisort

0

"вдохновил" в Хен ответ.

Это также позволяет вам изменять данные перед их сравнением, valueTransforms - это объект, если есть атрибут в этом объекте, который имеет функцию, он будет использоваться.

/* 
    * @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; 
    }, 
Смежные вопросы