8

我有两个对象数组,如下所示:

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 在两个数组之间对应,但不存在 1 对 1 的关系。可能有 0 个或多个 pkg 对应于任何给定项目。

(如果我有一个数据库,我会加入这些表,但在 JavaScript 中我只有两个相关的数组)。

我不确定如何构建比较器函数并传入第二个数组。

谢谢你的帮助。

4

3 回答 3

4

也许是这样的?

items = items.map(function (item, index) {
    return {
        item: item,
        pkg: pkgs[index] //I assumed associated pkgs were at the same index
    };
}).sort(function (a, b) {
   var pkgA = a.pkg, pkgB = b.pkg, r;

   r = +b.item.tobuy - +a.item.tobuy;

   if (r !== 0) return r;

   r = pkgA.store < pkgB.store? -1 : (pkgA.store === pkgB.store? 0 : 1);

   if (r !== 0) return r;

   r = +pkgA.aisle - +pkgB.aisle;

   if (r !== 0) return r;

   return pkgA.name < pkgB.name? -1 : (pkgA.name === pkgB.name? 0 : 1);

}).map(function (item) {
    return item.item;
});

除了合并数据,您还可以创建一个查找映射,允许直接从排序函数快速检索关联的包。

例如

var pkgsMap = pkgs.reduce(function (res, pkg) {
    res[pkg.item_id] = pkg;
    return res;
}, {});

然后在排序功能中您可以执行以下操作:

var pkgA = pkgsMap[a.id], pkgB = pkgsMap[b.id];

编辑:

pkgs 数组中实际上还有一个名为“ppu”的字段,它是每单位的价格。最低的 ppu 将被使用。

您可以使用以下内容构建您的包映射,然后使用排序函数中的映射来检索如上所述的关联包并实现排序算法。

var pkgsMap = pkgs.sort(function (a, b) {
    //not sure what ppu is so I sort it as a string
    return a.ppu < b.ppu? -1 : Number(a.ppu > b.ppu);
}).reduce(function (res, pkg) {
    if (!(pkg.item_id in res)) res[pkg.item_id] = pkg;
    return res;
}, {});
于 2013-10-09T02:18:13.280 回答
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+

现在我们加入了两个表,我们需要对它们进行排序。要对表格进行排序,我们使用内置的数组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/


编辑:如果您只想要id,tobuy和列,那么您可以使用如下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 回答