Подтвердить что ты не робот

Сортировщик коллекции backbone.js сортирует по нескольким полям?

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

Конкатенация строк отлично работает при сортировке нескольких полей в порядке возрастания, но это не сработало для меня, потому что 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;
}

Ответ 3

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

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

backbone-collection-multisort

Ответ 4

Главное, что 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" для ручной сортировки каждой группы уровней, а затем вручную заменить базовую коллекцию на этот вновь созданный массив. Возможно, вы можете настроить функцию, чтобы сделать это всякий раз, когда коллекция "изменяется".

Ответ 5

"вдохновил" на хенг-ответ.

Это также позволяет вам изменять данные перед их сравнением, 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;
    },