C# 迭代和比较两个类列表的更快方法
我需要遍历并比较2个类列表,比较2个并输出匹配的记录。这需要几个小时,我想不出一个方法来加快这个过程。名单上大约有60万条记录。下面是我的类代码和用于迭代和比较的代码C# 迭代和比较两个类列表的更快方法,c#,C#,我需要遍历并比较2个类列表,比较2个并输出匹配的记录。这需要几个小时,我想不出一个方法来加快这个过程。名单上大约有60万条记录。下面是我的类代码和用于迭代和比较的代码 class Person { string NPI; string address; string zip5; string lname; string lsk; string state; string fname; string zipfull; stri
class Person
{
string NPI;
string address;
string zip5;
string lname;
string lsk;
string state;
string fname;
string zipfull;
string seqNo;
public Person(string npi, string Address, string Zip5, string Lname, string LSK, string st, string Fname, string zipFull, string seqno)
{
this.NPI = npi;
this.address = Address;
this.zip5 = Zip5;
this.lname = Lname;
this.lsk = LSK;
this.state = st;
this.fname = Fname;
this.zipfull = zipFull;
this.seqNo = seqno;
}
public string getNPI()
{
return NPI;
}
public string getzip5()
{
return zip5;
}
public string getaddress()
{
return address;
}
public string Full()
{
string full = NPI + "," + address + "," + zip5 + "," + lname + "," + lsk + "," + state + "," + fname + "," + zipfull + "," + seqNo;
return full;
}
}
下面是用于遍历的代码。fuzz.ratio是我下载的一个模糊匹配nuget包,我知道这不是问题,因为我用它做了速度测试,速度非常快
string inputfile = @"C:\Input_File_150k.csv";
string blacklist = @"C:\Blacklist1.csv";
List<Person> input = Readcsv(inputfile);
List<Person> BL = Readcsv(blacklist);
string outputtest = @"C:\outputtest.csv";
StringBuilder csvcontent = new StringBuilder();
int lengthinput = input.Count();
for(int i = 0; i <lengthinput; i++)
{
int lengthbl = BL.Count();
for(int x = 0; x < lengthbl; x++)
{
if(input[i].getzip5() == BL[x].getzip5())
{
if(input[i].getNPI() == BL[x].getNPI())
{
if(Fuzz.Ratio(input[i].getaddress(),BL[x].getaddress()) > 90)
{
csvcontent.AppendLine(input[i].Full());
}
}
}
}
}
File.AppendAllText(outputtest, csvcontent.ToString());
您可以遍历每个列表一次,将所有值放入哈希映射中。例如,您可以使用邮政编码作为键,值是匹配该邮政编码的人员的数组 然后,您可以一次遍历一条记录,只需将每个hashmap bucket中的人相互比较
除非你所有的人都在同一个邮政编码中,如果是这样的话,希望你的其中一个键能起作用,这应该比N^2比较快。根据每个桶中有多少人,应该更接近于。我认为这部分代码可能比较慢 字符串完整=NPI+,+地址+,+zip5+,+lname+,+lsk+,+state+,+fname+,+zipfull+,+seqNo
如果您在这些属性上执行csvcontent.Append,并手动而不是使用Full,该怎么办?ps:它需要增加一种从外部阅读的可能性,因此除了上面的注释和使用字典之外,还可以使用public get/private set, 在你的循环中
int lengthinput = input.Count();
**int lengthbl = BL.Count();** //move out of the loops
for(int i = 0; i <lengthinput; i++)
{
**var inputi = input[i];** //move out of the inner loop
for(int x = 0; x < lengthbl; x++)
{
**var blx = BL[x];**
if(inputi.getzip5() == blx.getzip5())
{
if(inputi.getNPI() == blx.getNPI())
{
if(Fuzz.Ratio(inputi.getaddress(),blx.getaddress()) > 90)
{
csvcontent.AppendLine(inputi.Full());
}
}
}
}
}
我肯定会实现IEqualityComparer接口 这使得代码更加简洁,并且还可以从集合上可用的一些Linq扩展方法中获益 这些也支持类似并行性的东西
尝试从一个列表中创建一个字典以用作查找,同时迭代另一个列表。这将使复杂度从多项式变为线性
string inputfile = @"C:\Input_File_150k.csv";
string blacklist = @"C:\Blacklist1.csv";
List<Person> input = Readcsv(inputfile);
var blAddresses = Readcsv(blacklist).ToDictionary(
x => (Zip : x.getzip5(), NPI : x.getNPI()),
x => x.getaddress());
string outputtest = @"C:\outputtest.csv";
StringBuilder csvcontent = new StringBuilder();
int lengthinput = input.Count();
for(int i = 0; i <lengthinput; i++)
{
var zip = input[i].getzip5();
var npi = input[i].getNPI();
if(blAddresses.TryGetValue((zip,npi), out var blAddress)
{
if(Fuzz.Ratio(input[i].getaddress(),blAddress) > 90)
{
csvcontent.AppendLine(input[i].Full());
}
}
}
File.AppendAllText(outputtest, csvcontent.ToString());
我能想到的唯一方法就是将搜索并行化。您还可以降低您的时间复杂度,因为您现在的算法具有^2时间复杂度。您可以将其中一个转换为以zip和NPI为键的字典,然后将其用作查找,同时循环另一个。这会将复杂性从On*m更改为On+m,其中n和m是每个列表的长度。此外,您确实应该从使用get方法更改为只读属性公共字符串NPI{get;}如果您的代码有效,并且您希望查看代码的所有方面,并且您的问题符合他们的标准,你可以在上问这个问题。实际上他们需要对zip和NPI进行哈希运算。任何东西都比在N^2没有哈希映射要快,但是是的,通过多个值对存储桶进行哈希映射会更好。在列表上运行Count扩展方法时,如果您这么想的话,它只会获取Count属性值,而不会实际迭代集合以获取计数。所以我刚刚尝试了这个方法。问题是某些NPI为空,因此它告诉我,当zip和空白npi是相同的。有没有办法让我把它忽略掉NPIs@bigs89你想完全忽略这些,还是仍然需要匹配空白NPI,具体地说,输入文件是否有一个空白NPI,您想与黑名单中的空白NPI相匹配?@ BigS89i,我用一种替代方法更新了我的答案,它将使您当前的代码给出重复的ZIP/NPI值,或者您可以过滤掉空白的NPIs。但这真的取决于你希望得到的确切数据以及你想如何处理它。非常感谢你,这起到了作用,将大大加快我的过程。还有一个问题,如果我使用这个,并且想要模糊匹配地址以外的多个字段,我会只做x=>x.getaddress,p=>p吗。getlnam@bigs89您可以只执行x=>x来获取整个对象,也可以执行x=>Address:x.getaddress、Inam:x.getInam和命名。
string inputfile = @"C:\Input_File_150k.csv";
string blacklist = @"C:\Blacklist1.csv";
List<Person> input = Readcsv(inputfile);
var blAddresses = Readcsv(blacklist)
.GroupBy(x => (Zip : x.getzip5(), NPI : x.getNPI()))
.ToDictionary(
grp => grp.Key,
grp => grp.Select(y => y.getAddress()).ToList());
string outputtest = @"C:\outputtest.csv";
StringBuilder csvcontent = new StringBuilder();
int lengthinput = input.Count();
for(int i = 0; i <lengthinput; i++)
{
var zip = input[i].getzip5();
var npi = input[i].getNPI();
if(blAddresses.TryGetValue((zip,npi), out var blAddressList)
{
foreach(var blAddress in blAddressList)
{
if(Fuzz.Ratio(input[i].getaddress(),blAddress) > 90)
{
csvcontent.AppendLine(input[i].Full());
}
}
}
}
File.AppendAllText(outputtest, csvcontent.ToString());
var blAddresses = Readcsv(blacklist)
.Whree(x => x.getNPI().Length > 0)
.ToDictionary(
x => (Zip : x.getzip5(), NPI : x.getNPI()),
x => x.getaddress());