我有两个对象数组,如下所示:
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”
- 然后通过“store”
- 然后是“aisle”
- 然后通过“name”
虽然item_id和id在两个数组之间对应,但是没有1比1的关系。可能有0个或更多pkgs对应于任何给定项目。
(如果我有一个数据库,我只会加入表,但在JavaScript中我只有两个相关的数组。)
我不确定如何构建比较器函数并传入第二个数组。
感谢您的帮助。
答案 0 :(得分: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;
});
您还可以创建一个查找映射,以便直接从sort函数快速检索关联的包,而不是合并数据。
E.g。
var pkgsMap = pkgs.reduce(function (res, pkg) {
res[pkg.item_id] = pkg;
return res;
}, {});
然后在sort函数中你可以这样做:
var pkgA = pkgsMap[a.id], pkgB = pkgsMap[b.id];
修改强>
pkgs数组中实际上有另一个名为“ppu”的字段 是每单位的价格。最低的ppu是将要使用的ppu。
您可以使用以下内容构建包映射,然后使用sort函数中的映射来检索上述相关包,并实现排序算法。
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;
}, {});
答案 1 :(得分: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;
};
}
将 Arrays 转换为字典
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"}
] */
答案 2 :(得分:1)
让我们考虑一下如何在SQL中执行此操作:
SELECT * FROM items INNER JOIN pkgs ON items.id = pkgs.item_id
ORDER BY tobuy DESC, store, aisle, name
following answer演示了如何在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
加入items
和pkgs
,如下所示:
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
方法是unstable(即它可能不会保留输入列表中具有相等排序值的项目的排序)。因此,要解决此限制,我们将a.id - b.id
作为最后一个语句返回。
另请注意,我们正在使用<
和>
比较所有值(字符串或数字)。字符串按字典顺序进行比较,而数字则按数字进行比较。
汇总代码如下:
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
列,则可以使用{{1}从排序表中提取它如下:
map
这对应于以下SQL查询:
table.map(function (item) {
return {
id: item.id,
tobuy: item.tobuy,
name: item.name
};
});
请参阅更新后的演示:http://jsfiddle.net/7ZG96/1/