根据匹配的 ID 合并两个数组中的项目
Posted
技术标签:
【中文标题】根据匹配的 ID 合并两个数组中的项目【英文标题】:Merge items from two arrays based on matching ID 【发布时间】:2019-09-06 13:55:51 【问题描述】:我有一个这样的数据对象:
"data1": [
[
"ID",
"name",
"Birthday"
],
[
"10",
"thomas",
"1992-03-17"
],
[
"11",
"Emily",
"2000-03-03"
]
],
"data2": [
[
"Balance",
"ID"
],
[
"$4500",
"10"
],
[
"$1500",
"13"
]
]
它包含两个数组data1
和data2
。
每个数组的第一行是列的名称,其余行包含数据(将其视为表格)。
我想比较两个数组中的ID
字段,如果ID
s 匹配,那么最终输出将包含Balance
列,余额对应于ID
,如果ID
s不匹配则Balance
将是$0
。
预期输出:
"output": [
[
"ID",
"name",
"Birthday",
"Balance"
],
[
"10",
"thomas",
"1992-03-17",
"$4500" //ID 10 matched so the balance added here
],
[
"11",
"Emily",
"2000-03-03",
"0" //0 bcoz the ID 11 is not there in data2 array
]
]
我觉得这很难完成。把它想象成 mysql 中的 LEFT-JOIN。 我提到了这个solution,但它在我的情况下不起作用,因为我的回复中没有密钥。
编辑:我还需要加入其他领域。
【问题讨论】:
【参考方案1】:您可以使用Array.prototype.map()、find、filter、slice、reduce、concat、includes 和 Object.assign()。
这个解决方案:
处理项目的任意顺序。订单从标题中读取。 仅当data2
中存在Balance
字段时才附加一个字段。
加入所有其他字段(由 OP 要求,参见下面的 cmets)。
将默认值作为输入,如果data1
和data2
中不存在数据,则使用它们。
function merge( data1, data2 , defaults)
// get the final headers, add/move 'Balance' to the end
const headers = [...data1[0].filter(x => x !== 'Balance')]
.concat(data2[0].includes('Balance') ? ['Balance'] : []);
// map the data from data1 to an array of objects, each key is the header name, also merge the default values.
const d1 = data1.slice(1)
.map(x => x.reduce((acc, y, i) => ( ...defaults, ...acc, [data1[0][i]]: y ), ));
// map the data from data2 to an array of objects, each key is the header name
const d2 = data2.slice(1)
.map(x => x.reduce((acc, y, i) => ( ...acc, [data2[0][i]]: y ), ));
// combine d1 and d2
const output = d1.map((x, i) => // iterate over d1
// merge values from d2 into this value
const d = Object.assign(x, d2.find(y => y['ID'] === x['ID']));
// return an array ordered according to the header
return headers.map(h => d[h]);
);
return output: [headers, ...output] ;
const test0 =
data1: [[ "ID","name","Birthday","other"],["10","thomas","1992-03-17","empty"],["11","Emily","2000-03-03","empty"]],
data2: [["other", "ID", "Balance", "city"],["hello", "10", "$4500", "New York"],["world", "10","$8","Brazil"]]
;
const test1 =
data1: [["ID","name","Birthday"],["10","thomas","1992-03-17"],["11","Emily","2000-03-03"]],
data2: [["other","ID"],["x","10"],["y","11"]]
;
console.log(merge(test0, Balance: '$0' ));
console.log(merge(test1, Balance: '$0' ));
【讨论】:
这个解决方案是不可扩展的,如果你添加像“Balance”这样的属性,它不会接受它们。 @karkael,我更新了代码以处理项目的任意排序。 将此标记为已接受的答案,因为它非常适合我的给定场景。它很整洁,我可以重复使用该功能 请在代码中添加 cmets 行 return i === 0 好吗? [...x, 'Balance'] : [...x, (data.data2.find((y) => y[idIdx2] === x[idIdx1]) || [])[balanceIdx2] | | '$0' 解释了代码的确切作用,因为我在理解这部分时遇到了一点困难: return i === 0 ? [...x, '平衡'] 非常感谢@jo_va。你真的很有帮助。【参考方案2】:const KEY_ID = "ID";
var data =
"data1": [
[ "ID", "name", "Birthday" ],
[ "10", "thomas", "1992-03-17" ],
[ "11", "Emily", "2000-03-03" ]
],
"data2": [
[ "Balance", "ID" ],
[ "$4500", "10" ],
[ "$1500", "13" ]
]
var merged = Object.keys(data).map(function (key)
var tmp = data[key].slice();
var heads = tmp.shift();
return tmp.map(function (item)
var row = ;
heads.forEach(function (head, i)
row[head] = item[i];
);
return row;
);
).flat().reduce(function (acc, row)
var found = acc.find(function (item)
return row[KEY_ID] === item[KEY_ID];
)
if (!found)
found = row;
acc.push(found);
else
Object.keys(row).forEach(function (head)
found[head] = row[head];
);
return acc;
, []);
console.log(merged);
此解决方案是可扩展的:如果您添加属性,它将扩展新格式。
【讨论】:
【参考方案3】:let a = "data1": [ ... ],"data2": [ ...]
let r = a.data1.reduce((r,u,i)=>
if(i !== 0)
let entry = a.data2.filter((a)=> a[1]===u[0])
r.push([...u,entry.length ? entry[0][0] : 0])
return r
,[[
"ID",
"name",
"Birthday",
"Balance"
]])
【讨论】:
【参考方案4】:您可以将所有表操作抽象为类:
function Table(array)
const [head, ...values] = array;
const Entry =(entry) => (
get(key) return entry[ head.indexOf(key) ]; ,
set(key, value) entry[ head.indexOf(key) ] = value;
);
return
index(name)
const result = ;
for(const value of values)
result[ value[ head.indexOf(name) ] ] = Entry(value);
return result;
,
*[Symbol.iterator]()
for(const row of values)
yield Entry(row);
,
addRow(key) head.push(key);
;
可用作:
const users = Table(obj.data1);
const balances = Table(obj.data2);
const balanceByID = balance.index("ID");
users.addRow("Balance");
for(const user of users)
user.set("Balance", balanceByID[ user.get("ID") ].get("Balance"));
【讨论】:
以上是关于根据匹配的 ID 合并两个数组中的项目的主要内容,如果未能解决你的问题,请参考以下文章