8

以下のようなオブジェクトの配列が 2 つあります。

items = [{"id":"5","tobuy":"1","name":"pop"},
         {"id":"6","tobuy":"1","name":"fish"},
         {"id":"7","tobuy":"0","name":"soda"}]
pkgs =  [{"item_id":"5","store":"Market","aisle":"3"},
         {"item_id":"6","store":"Market","aisle":"2"},
         {"item_id":"6","store":"Dept","aisle":"8"},
         {"item_id":"7","store":"Market","aisle":"4"}]

items 配列をソートしようとしていますが、pkgs 配列のデータを活用したいと考えています。
pkgs 配列の「item_id」フィールドは、items 配列の「id」フィールドに対応します。
たとえば、並べ替えたい:

  • 最初に「tobuy」の降順
  • 次に「ストア」で
  • 次に「通路」
  • 次に「名前」で

item_id と id は 2 つの配列間で対応していますが、1 対 1 の関係はありません。特定の項目に対応する 0 個以上の pkg が存在する可能性があります。

(データベースがあればテーブルを結合するだけですが、JavaScript では 2 つの関連する配列しかありません)。

コンパレータ関数を作成して 2 番目の配列を渡す方法がわかりません。

助けてくれてありがとう。

4

3 に答える 3

1

コンパレータを生成する関数を作成します。これは扱いにくいように見えますが、必要な並べ替え順序を生成できることを意味します

function generateComparator(dict, index, order) {
    return function (a, b) {
        var i, key, direction,
            ai = a[index], av,
            bi = b[index], bv;
        for (i = 0; i < order.length; ++i) {
            key = order[i].key;
            direction = +!!order[i].reverse || -1;
            if (dict[ai].hasOwnProperty(key)) // if in dict, lookup
                av = dict[ai][key];
            else                              // else look up in item
                av = a[key];
            if (dict[bi].hasOwnProperty(key))
                bv = dict[ai][key];
            else
                bv = b[key];
            // console.log(i, key, av, bv, direction); // debug
            if (av === bv)
                continue;
            if (av < bv)
                return direction;
            return -direction;
        }
        return 0;
    };
}

配列を辞書に変換する

var dict = (function (arr, index) {
    var o = {}, i;
    for (i = 0; i < arr.length; ++i) {
        o[arr[i][index]] = arr[i];
    }
    return o;
}(pkgs, 'item_id'));

並べ替えの選択を定義する

var order = [
    {key: 'tobuy', reverse: 1},
    {key: 'store'},
    {key: 'aisle'},
    {key: 'name'}
];

辞書を使用してコンパレータを生成する

var comparator = generateComparator(dict, 'id', order);

次に、最初の配列を並べ替えます

items.sort(comparator);
/* [
    {"id": "6", "tobuy": "1", "name": "fish"},
    {"id": "5", "tobuy": "1", "name": "pop"},
    {"id": "7", "tobuy": "0", "name": "soda"}
] */
于 2013-10-09T02:27:28.763 に答える
1

SQL でこれを行う方法を考えてみましょう。

SELECT * FROM items INNER JOIN pkgs ON items.id = pkgs.item_id
ORDER BY tobuy DESC, store, aisle, name

次の回答は、JavaScript で内部結合と等結合を実装する方法を示しています。

function equijoin(primary, foreign, primaryKey, foreignKey, select) {
    var m = primary.length, n = foreign.length, index = [], c = [];

    for (var i = 0; i < m; i++) {     // loop through m items
        var row = primary[i];
        index[row[primaryKey]] = row; // create an index for primary table
    }

    for (var j = 0; j < n; j++) {     // loop through n items
        var y = foreign[j];
        var x = index[y[foreignKey]]; // get corresponding row from primary
        c.push(select(x, y));         // select only the columns you need
    }

    return c;
}

これで、次のように使用equijoinして参加できます。itemspkgs

equijoin(items, pkgs, "id", "item_id", function (item, pkg) {
    return {
        id: +item.id,
        tobuy: +item.tobuy,
        store: pkg.store,
        aisle: +pkg.aisle,
        name: item.name
    };
});

単項演算子を適用することitem.idで、 ,item.tobuyとを数値に強制していることに注意してください。pkg.aisle+

2 つのテーブルを結合したので、それらを並べ替える必要があります。テーブルを並べ替えるには、組み込みの配列sortメソッドを使用します。

.sort(function (a, b) {
    // ORDER BY tobuy DESC

    var aTobuy = a.tobuy, bTobuy = b.tobuy;

    if (aTobuy < bTobuy) return 1;
    if (aTobuy > bTobuy) return -1;

    // ORDER BY store

    var aStore = a.store, bStore = b.store;

    if (aStore < bStore) return -1;
    if (aStore > bStore) return 1;

    // ORDER BY aisle

    var aAisle = a.aisle, bAisle = b.aisle;

    if (aAisle < bAisle) return -1;
    if (aAisle > bAisle) return 1;

    // ORDER BY name

    var aName = a.name, bName = b.name;

    if (aName < bName) return -1;
    if (aName > bName) return 1;

    // keep them unchanged

    return a.id - b.id;
});

このsortメソッドは不安定です(つまり、入力リスト内のソート値が等しい項目の順序が保持されない可能性があります)。したがって、この制限を回避するためにa.id - b.id、最後のステートメントとして戻ります。

<また、 andを使用してすべての値 (文字列または数値) を比較していることにも注意してください>。文字列は辞書式に比較され、数値は数値的に比較されます。

コードをまとめると次のようになります。

var table = equijoin(items, pkgs, "id", "item_id", function (item, pkg) {
    return {
        id: +item.id,
        tobuy: +item.tobuy,
        store: pkg.store,
        aisle: +pkg.aisle,
        name: item.name
    };
}).sort(function (a, b) {
    var aTobuy = a.tobuy, bTobuy = b.tobuy;

    if (aTobuy < bTobuy) return 1;
    if (aTobuy > bTobuy) return -1;

    var aStore = a.store, bStore = b.store;

    if (aStore < bStore) return -1;
    if (aStore > bStore) return 1;

    var aAisle = a.aisle, bAisle = b.aisle;

    if (aAisle < bAisle) return -1;
    if (aAisle > bAisle) return 1;

    var aName = a.name, bName = b.name;

    if (aName < bName) return -1;
    if (aName > bName) return 1;

    return a.id - b.id;
});

SQLほど簡潔ではありませんか?とにかく、デモをご覧ください: http://jsfiddle.net/7ZG96/


編集:idtobuyおよび列のみが必要な場合は、次のようnameに並べ替えられたテーブルから抽出できます。map

table.map(function (item) {
    return {
        id: item.id,
        tobuy: item.tobuy,
        name: item.name
    };
});

これは、次の SQL クエリに対応します。

SELECT id, tobuy, name FROM (SELECT * FROM items INNER JOIN pkgs
ON items.id = pkgs.item_id ORDER BY tobuy DESC, store, aisle, name)

更新されたデモを参照してください: http://jsfiddle.net/7ZG96/1/

于 2013-10-09T03:06:45.203 に答える