簡體   English   中英

迭代和比較兩個類列表的更快方法

[英]faster way to iterate through and compare two lists of classes

我需要遍歷並比較 2 個類列表,然后比較 2 個並輸出匹配的記錄。 這需要幾個小時,我想不出加快這個過程的方法。 這些列表大約有 60 萬條記錄。 這是我的類代碼和用於迭代和比較的代碼。

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一條記錄,只需要將每個hashmap桶中的人相互比較即可。

除非你所有的人都在同一個郵政編碼中(如果是這樣,希望你的一個鍵可以用於這個),這應該比 N^2 比較快。 應該更接近 O(N),具體取決於每個桶中有多少人。

除了上面的評論( https://stackoverflow.com/a/60886352/5334191和使用字典),在你的循環中

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 擴展方法中受益。

那些也支持諸如並行性之類的東西。

https://marcofranssen.nl/delegate-your-equality-comparisons/

嘗試從一個列表創建一個字典,在迭代另一個列表時用作查找。 這會將復雜性從多項式變為線性。

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());

具體來說,我創建了一個字典,該字典在 Zip 和 NPI 上鍵入並獲取所需的地址。 我正在使用一些 C# 7 的東西,比如值元組,但如果需要,可以將其更改為引用元組、匿名類或自定義類。

編輯

這是使這項工作的更改,就像您當前的代碼假設您有重復的 Zip/NPI 值一樣

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());

或者,如果您只需要過濾掉黑名單中 NPI 為空白的任何內容以獲得唯一鍵,您可以改為執行此操作

var blAddresses = Readcsv(blacklist)
    .Whree(x => x.getNPI().Length > 0) 
    .ToDictionary(
        x => (Zip : x.getzip5(), NPI : x.getNPI()),
        x => x.getaddress());

我認為這部分代碼可能很慢

字符串全 = NPI + "," + 地址 + "," + zip5 + "," + lname + "," + lsk + "," + state + "," + fname + "," + zipfull + "," + 序列號;

如果你執行csvcontent.Append這些屬性和, “手動”,而不是使用Full() ps:它需要增加從外部讀取它的可能性,所以 public get / private set

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM