web-dev-qa-db-ja.com

Backbone.jsコレクションコンパレータは複数のフィールドでソートしますか?

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

上記のコードは、レベルごとにアイテムを並べ替えます。レベルで並べ替えてから、タイトルで並べ替えます。それをしてもいいですか?ありがとう。

42
Harry

@ amchang87の答えは間違いなく機能しますが、私が見つけた別の機能は、ソート可能なフィールドの配列を返すことです:

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

これは、(コンテンツに基づいて)配列のソート順でのJSの動作に依存していると思われるため、複数のブラウザでまだテストしていません。これは間違いなくWebKitで機能します。

73
Bo Jeanes

複数のフィールドを昇順でソートする場合、文字列の連結は正常に機能しますが、1)フィールドごとにasc/descをサポートする必要があり、2)特定のフィールドが数値フィールドであったため(つまり、2の後に10が必要です)昇順の場合)。そこで、以下は私が使用したコンパレーター関数であり、私のニーズに合わせてうまく機能しました。バックボーンコレクションには、「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;
}
7
hyong

降順と昇順を並べ替える必要がある場合、配列を返すことは一貫していません...

関連する比較整数をBackbone Comparator関数に戻すために使用できる関数の小さなセットを作成しました。

バックボーンコレクションマルチソート

2
Lawrence

主なことは、バックボーンが、あるアイテムから別のアイテムへの単一の相対値でソートすることです。したがって、1つのコレクションで2回並べ替えることは直接不可能ですが、これを試してみます。

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" vs "200title"。しかし、主なアイデアは、1つの基準に基づいて互いに比較できる2つの同等のオブジェクトを作成する必要があるということです。

他の解決策は、アンダースコアを使用してレベルを「グループ化」し、「sortby」を使用して各レベルグループを手動で並べ替え、基になるコレクションをこの新しく作成された配列に手動で置き換えることです。おそらく、コレクションが「変更」されるたびにこれを行う関数をセットアップできます。

1
adrian

ヒョンの答えに「触発された」。

これにより、比較する前にデータを変更することもできます。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;
    },
0
useless