javascript中数据分组的优化算法
以下(简化)json类型的数据定义了联系人:javascript中数据分组的优化算法,javascript,algorithm,grouping,lodash,Javascript,Algorithm,Grouping,Lodash,以下(简化)json类型的数据定义了联系人: { id: number; name: string; phone: string; email: string } 有以下一组数据: +---+----------+-------------+---------------------------+ |id | name | phone |email | +---+----------+------------
{
id: number;
name: string;
phone: string;
email: string
}
有以下一组数据:
+---+----------+-------------+---------------------------+
|id | name | phone |email |
+---+----------+-------------+---------------------------+
|1 | John | 11111111 |aaaa@test.com |
|2 | Marc | 22222222 |bbbb@test.com |
|3 | Ron | 99999999 |aaaa@test.com |
|4 | Andrew | 55555555 |dddd@test.com |
|5 | Wim | 99999999 |gggg@test.com |
|6 | Marc | 33333333 |cccc@test.com |
|7 | Dan | 44444444 |cccc@test.com |
+---+----------+-------------+---------------------------+
目标是根据以下约束条件,使用javascript(可以选择lodash,但主要思想是明确算法)查找属于一起的组:当以下条件相同时,联系人属于组:姓名、电话或电子邮件。结果显示id被分组为数组中的数组。一组1中的联系人将被忽略
在上面的示例中,这意味着ID为1,3,5的联系人属于一起,因为1,3共享相同的电子邮件,3和5共享相同的电话号码。同样,2,6,7:2和6有相同的名字,6和7有相同的电子邮件。5没有任何共同之处。
因此,预期结果是:[[1,3,5],[2,6,7]]
背景:
一个有效的解决方案是迭代每个项目,如果名称、电子邮件或电话相同,则检查列表的其余部分。如果是这样,将它们分组并从列表中删除(在本例中,我们将1与列表中的所有项目进行比较,只找到3项)。问题是,还需要再次检查这些组的下一个项目,因为在这种情况下,还没有检测到5是组的一部分。这使得算法变得复杂,而我怀疑有一种简单的方法可以在线性时间内解决这个问题。这类问题也可能有一个名称?
`实现所需功能的一种方法是将联系人分组。 每个组将包含
姓名
、电话
和电子邮件
的列表
然后遍历联系人,查看当前联系人是否属于任何组。如果没有,请创建一个新组并设置其名称/电话/电子邮件
,以便下一个联系人可能会在同一时间进入同一组
var数据=[
{id:1,姓名:'John',电话:'11111111',电子邮件:'aaaa@test.com'},
{id:2,姓名:'Marc',电话:'2222',电子邮件:'bbbb@test.com'},
{id:3,姓名:'Ron',电话:'9999999',电子邮件:'aaaa@test.com'},
{id:4,姓名:'Andrew',电话:'5555',电子邮件:'dddd@test.com'},
{id:5,姓名:'Wim',电话:'9999999',电子邮件:'gggg@test.com'},
{id:6,姓名:'Marc',电话:'33333333',电子邮件:'cccc@test.com'},
{id:7,姓名:'Dan',电话:'4444',电子邮件:'cccc@test.com'}
];
var组=[];
data.forEach(功能(人员){
var phone=person.phone;
var email=person.email;
var name=person.name;
var id=person.id;
var=false;
组。forEach(函数(g){
如果(g.names.indexOf(name)>-1
||g.phones.indexOf(电话)>-1
||g.emails.indexOf(email)>-1){
发现=真;
g、 name.push(name);
g、 电话。推(电话);
g、 电子邮件。推送(电子邮件);
g、 人。推(id);
}
});
如果(!找到){
推送({names:[name]、电话:[phone]、电子邮件:[email]、人员:[id]});
}
});
var输出=[];
组。forEach(函数(g){
输出。推送(g.人);
});
控制台日志(输出)//[1,3,5],[2,6,7],[4]
创意:
- 从0组开始
- 迭代你的联系人列表
- 检查是否有联系人姓名、电话或电子邮件的群组。将这些组的所有成员合并为同一组。然后把你自己加入这个小组。如果没有,请自己开始一个新的群组,并将姓名、电话和电子邮件群组设置为自己
var数据=[
{id:1,姓名:'John',电话:'11111111',电子邮件:'aaaa@test.com'},
{id:2,姓名:'Marc',电话:'9999999',电子邮件:'bbbb@test.com'},
{id:3,姓名:'Ron',电话:'9999999',电子邮件:'aaaa@test.com'},
{id:4,姓名:'Andrew',电话:'5555',电子邮件:'dddd@test.com'},
{id:5,姓名:'Wim',电话:'9999999',电子邮件:'gggg@test.com'},
{id:6,姓名:'Marc',电话:'33333333',电子邮件:'cccc@test.com'},
{id:7,姓名:'Dan',电话:'4444',电子邮件:'cccc@test.com'}
];
//UNION-FIND结构,具有路径压缩和按秩并集
var UNIONFIND=(函数(){
函数_find(n)
{
如果(n.parent==n)返回n;
n、 父项=_find(n.parent);
返回n.parent;
}
返回{
makeset:函数(id){
var newnode={
父项:null,
id:id,
排名:0
};
newnode.parent=newnode;
返回newnode;
},
查找:_find,
组合:函数(n1,n2){
var n1=_find(n1);
var n2=_find(n2);
如果(n1==n2)返回;
if(n1.秩const data = [
{id:1,name:'John',phone:'11111111',email:'aaaa@test.com'},
{id:2,name:'Marc',phone:'99999999',email:'bbbb@test.com'},
{id:3,name:'Ron',phone:'99999999',email:'aaaa@test.com'},
{id:4,name:'Andrew',phone:'55555555',email:'dddd@test.com'},
{id:5,name:'Wim',phone:'99999999',email:'gggg@test.com'},
{id:6,name:'Marc',phone:'33333333',email:'cccc@test.com'},
{id:7,name:'Dan',phone:'44444444',email:'cccc@test.com'}
];
const groups = function(inputs) {
let valuesToGroups = new Map(
['name', 'phone', 'email'].map(key => [key, new Map()]));
let groups = new Map();
let pendingMerges = [];
for (const entry of inputs) {
let group = undefined;
let found = [];
for (const [key, valueMap] of valuesToGroups) {
// look up value in values-index for current key
group = valueMap.get(entry[key]);
if (group !== undefined) {
found.push(group);
// not breaking allows groups to be merged
}
}
if (found.length === 0) {
// not found: create new group
group = groups.size;
groups.set(group, [entry.id]);
} else {
// found: add entry to chosen group
group = found[0];
groups.get(group).push(entry.id);
if (found.length > 1) {
pendingMerges.push(found);
}
}
// add entry's values to index, pointing to group
for (const [key, valueMap] of valuesToGroups) {
valueMap.set(entry[key], group);
}
}
// do pending merges; initially, all groups are stand-alone
let merges = new Map(Array.from(groups.keys()).map(k => [k, k]));
for (const merge of pendingMerges) {
// contents will go to the lowest-numbered group
const sorted = merge.map(groupId => merges.get(groupId)).sort();
sorted.forEach(groupId => merges.set(groupId, sorted[0]));
}
const cleanGroups = new Map();
groups.forEach((value, key) => {
const k = merges.get(key);
if ( ! cleanGroups.has(k)) {
cleanGroups.set(k, []);
}
value.forEach(id => cleanGroups.get(k).push(id))
})
// return only non-empty groups
return [... cleanGroups].filter(g => g[1].length>1).map(g => [... g[1]]);
}(data);
console.log(""+JSON.stringify(groups))
// output is [[1,2,3,5,6,7]]