JavaScript 中两个或多个数组的 SQL 连接
Posted
技术标签:
【中文标题】JavaScript 中两个或多个数组的 SQL 连接【英文标题】:SQL-joining of two or more arrays in JavaScript 【发布时间】:2016-05-26 15:03:41 【问题描述】:我有一个包含几个数组的单页网络应用程序,这些数组在逻辑上是链接的:来自“用户”的记录指的是“user_types”中的记录,“费用”指的是“用户”等:
var users = [
id: "u0001", name: "John", user_type_id: "1" ,
id: "u0002", name: "Bob", user_type_id: "1" ,
id: "u0003", name: "Alice", user_type_id: "5" ,
id: "u0004", name: "Jennifer", user_type_id: "5" ,
// ... more
];
var user_types = [
id: "1", name: "Regular Clients",
id: "5", name: "VIP Clients",
// ... more
];
var charges = [
id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", ,
id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", ,
id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", ,
id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", ,
id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", ,
// ... more
];
我需要以链接的方式显示它们,类似于以下SQL的产物:
SELECT
charges.date,
charges.amount,
users.name,
user_types.name
FROM
charges
LEFT OUTER JOIN users ON users.id = charges.user_id
LEFT OUTER JOIN user_types ON user_types.id = users.user_type_id
我知道我可以在服务器上使用此 SQL 查询创建 API 调用,但我想避免这种情况,因为表已经加载到 Web 应用程序中。
将它们加入记忆的最简单方法是什么?
【问题讨论】:
你愿意使用库还是纯JS?这些数组中有多少数据?它们很大吗? 你已经回答了你自己的问题:用 SQL 来做。然后更新您应用中的数据以匹配。 另外,如何生成/填充数组?如果您可以修改它们的填充方式,您可以在需要加入它们时使事情变得更容易。 IMTheNachoMan:小型JS库还可以。 “Charges”数组通常是几百条记录,但应该能够处理几千条。 “用户”只有几百个。 【参考方案1】:如果小库没问题,可以用StrelkiJS:
var users = new StrelkiJS.IndexedArray();
users.loadArray([
id: "u0001", name: "John", user_type_id: "1" ,
id: "u0002", name: "Bob", user_type_id: "1" ,
id: "u0003", name: "Alice", user_type_id: "5" ,
id: "u0004", name: "Jennifer", user_type_id: "5" ,
// ... more
]);
var user_types = new StrelkiJS.IndexedArray();
user_types.loadArray([
id: "1", name: "Regular Clients",
id: "5", name: "VIP Clients",
// ... more
]);
var charges = new StrelkiJS.IndexedArray();
charges.loadArray([
id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", ,
id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", ,
id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", ,
id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", ,
id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", ,
// ... more
]);
var result = charges.query([
from_col: "user_id",
to_table: users,
to_col: "id",
type: "outer",
join: [
from_col: "user_type_id",
to_table: user_types,
to_col: "id",
type: "outer",
]
])
结果将加入以下结构的数组:
[
[
"id":"7443","user_id":"u0001","date":"2016-01-01","amount":"3.99",
"id":"u0001","name":"John","user_type_id":"1",
"id":"1","name":"Regular Clients"
],
[
"id":"7445","user_id":"u0001","date":"2016-01-01","amount":"4.02",
"id":"u0001","name":"John","user_type_id":"1",
"id":"1","name":"Regular Clients"
],
[
"id":"7448","user_id":"u0001","date":"2016-01-01","amount":"6.99",
"id":"u0001","name":"John","user_type_id":"1",
"id":"1","name":"Regular Clients"
],
[
"id":"7453","user_id":"u0003","date":"2016-01-01","amount":"3.00",
"id":"u0003","name":"Alice","user_type_id":"5",
"id":"5","name":"VIP Clients"
],
[
"id":"7469","user_id":null,"date":"2016-01-01","amount":"3.99",
null,
null
]
]
【讨论】:
这看起来很完美。谢谢!【参考方案2】:如果您可以修改 users
和 user_types
的填充方式,那么您可以很快地做到这一点。
您需要将 users
和 user_types
更改为对象,这样您就有了这样的东西:
// make users an object with the id as the key
var users =
"u0001" : name: "John", user_type_id: "1" ,
"u0002" : name: "Bob", user_type_id: "1" ,
"u0003" : name: "Alice", user_type_id: "5" ,
"u0004" : name: "Jennifer", user_type_id: "5"
;
// same for user_types
var user_types =
"1" : name: "Regular Clients" ,
"5" : name: "VIP Clients"
;
var charges = [
id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", ,
id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", ,
id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", ,
id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", ,
id: "7469", user_id: null , date: "2016-01-01", amount: "3.99",
];
// now you can just loop through and use object key lookups:
var out = [];
for(var i = 0, numCharges = charges.length; i < numCharges; ++i)
var currentCharge = charges[i];
if(currentCharge.user_id === null) continue;
out.push([
currentCharge.date,
currentCharge.amount,
// get the current charges user_id and look up the name from users
users[currentCharge.user_id].name,
// same as above but use the user_type_id to get the user_type name
user_types[users[currentCharge.user_id].user_type_id].name
]);
console.log(out);
【讨论】:
【参考方案3】:该提案以IMTheNachoMan 解决方案为特色,扩展了从给定数据生成必要对象的功能。
它包括charges
的所有行,因为使用 SQL,也会返回行。
null
值的问题在这里测试,然后返回 null
。
var users = [ id: "u0001", name: "John", user_type_id: "1" , id: "u0002", name: "Bob", user_type_id: "1" , id: "u0003", name: "Alice", user_type_id: "5" , id: "u0004", name: "Jennifer", user_type_id: "5" ],
user_types = [ id: "1", name: "Regular Clients" , id: "5", name: "VIP Clients" ],
charges = [ id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", , id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", , id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", , id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", , id: "7469", user_id: null, date: "2016-01-01", amount: "3.99", ],
user = Object.create(null),
type = Object.create(null),
result;
users.forEach(function (u)
user[u.id] = u;
);
user_types.forEach(function (t)
type[t.id] = t;
);
result = charges.map(function (charge)
return
'charges.date': charge.date,
'charges.amount': charge.amount,
'users.name': charge.user_id === null ? null : user[charge.user_id].name,
'user_types': charge.user_id === null ? null : type[user[charge.user_id].user_type_id].name,
;
);
console.log(result);
【讨论】:
【参考方案4】:将users
制作成地图,以便您可以使用users['u0001']
。然后循环通过charges
并执行users[current_charge.user_id].charges.push(current_charge)
。 users
中的每个用户都应该有一个 charges
属性初始化为一个空数组。当您将 users
数组转换为 id => user
映射时,您可以这样做。
这里不需要什么特别的东西,只需通过users
和charges
循环两次:
var users_map = ;
var i;
for(i = 0; i < users.length; i++)
users_map[users[i].id] = users[i];
users_map[users[i].id].charges = [];
for(i = 0; i < charges.length; i++)
users_map[charge[i].user_id].charges.push(charge[i]);
如果你真的需要最终的“结果”是一个数组,而不是一个map,你可以再次循环users_map
,把它变成一个数组。
利用现代 JS 东西的一个非常简单的解决方案是:
var joined_data = Object.keys(users_map).map(function (key)
return users_map[key];
);
您可以使用 lodash 或其他类似的库使上述代码更漂亮。
【讨论】:
【参考方案5】:不重构对象的唯一方法是循环和过滤。 您可以通过首先处理用户及其类型来稍微优化它,但仅此而已...
var users = [
id: "u0001", name: "John", user_type_id: "1" ,
id: "u0002", name: "Bob", user_type_id: "1" ,
id: "u0003", name: "Alice", user_type_id: "5" ,
id: "u0004", name: "Jennifer", user_type_id: "5" ,
// ... more
];
var user_types = [
id: "1", name: "Regular Clients",
id: "5", name: "VIP Clients",
// ... more
];
var charges = [
id: "7443", user_id: "u0001", date: "2016-01-01", amount: "3.99", ,
id: "7445", user_id: "u0001", date: "2016-01-01", amount: "4.02", ,
id: "7448", user_id: "u0001", date: "2016-01-01", amount: "6.99", ,
id: "7453", user_id: "u0003", date: "2016-01-01", amount: "3.00", ,
id: "7469", user_id: null , date: "2016-01-01", amount: "3.99", ,
// ... more
];
// pre-process users
var usersPlusTypes = users.map(function(u)
var foundUserTypes = user_types.filter(function(ut)
return ut.id == u.user_type_id;
);
return
id: u.id,
user: u,
userType: foundUserTypes.length ? foundUserTypes[0] : null
)
// now link charges to users
var results = charges.map(function(c)
var user = usersPlusTypes.filter(function(upt)
return upt.id == c.user_id;
);
return
date: c.date,
amount: c.amount,
userName: user.length ? user[0].user.name : null,
userTypeName: user.length && user[0].userType ? user[0].userType.name : null,
;
);
console.log(results);
【讨论】:
以上是关于JavaScript 中两个或多个数组的 SQL 连接的主要内容,如果未能解决你的问题,请参考以下文章