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】:

如果您可以修改 usersuser_types 的填充方式,那么您可以很快地做到这一点。

您需要将 usersuser_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 =&gt; user 映射时,您可以这样做。

这里不需要什么特别的东西,只需通过userscharges 循环两次:

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 连接的主要内容,如果未能解决你的问题,请参考以下文章

concat() 方法用于连接两个或多个数组。

JavaScript数组方法最全集合

JavaScript数组方法最全集合

JavaScript常用数组操作方法

javaScript 数组

如何在 Javascript 或 Jquery 中处理具有多个循环的大量数组?