使用StartsWith从另一个列表更新一个列表的项目的最快方法

时间:2017-07-21 13:04:10

标签: c# asp.net list ienumerable

我有一个场景,我需要根据另一个列表中的数据更新几个项目。我已经在这里经历了各种各样的问题,但没有人帮忙。

方案

listA:总计数约为88000

public class CDRs
 {
    public string cld { get; set; }
    public string prefix2 { get; set; }
    public string country { get; set; }
    public string city { get; set; }
 }

listB:总数:3000。

public class RatesVM
    {
        public string prefix { get; set; }
        public string Country { get; set; }
        public string City { get; set; }
    }

现在在 listB 中, listA 字段可以有多个匹配 cld

例如。 listA.cld =“8801123232”; 来自ListB的匹配前缀是

880     BGD Proper
8801    BGD Mobile
88011   BGD Dhaka Mobile
88017   BGD Dhaka Mobile
88018   BGD Dhaka Mobile
88019   BGD Dhaka Mobile

现在我希望在这种情况下最接近的匹配是

88011   BGD Dhaka Mobile

方法我现在正在关注。

foreach (var x in listA)
            {
                var tempObj = listB.FirstOrDefault(y => x.cld.StartsWith(y.prefix));
                if (tempObj != null)
                {
                    x.prefix2 = tempObj.prefix;
                    x.country = tempObj.Country;
                    x.city = tempObj.City;
                }
                else
                {
                    x.prefix2 = "InBound";
                    x.country = "Unknown";
                    x.city = "Unknown";
                }
            }

它工作正常,但需要很多时间。对于这种情况,大约2-3分钟

在很少的情况下,ListA将拥有大约100万条记录。我担心这需要永远。

非常感谢提前

2 个答案:

答案 0 :(得分:1)

我会建议以下代码。关键区别在于使用orderedListB来确保您获得最具体的匹配(即首先以最长的前缀开头),以及Dictionary来缓存结果。 *

Dictionary<string, RatesVM> cache = new Dictionary<string, RatesVM>();
var orderedListB = listB.OrderByDescending(z => z.prefix.Length).ToList();

foreach (var x in listA)
{
    RatesVM cached;
    cache.TryGetValue(x.cld, out cached);
    var tempObj = cached ?? orderedListB.FirstOrDefault(z => x.cld.StartsWith(z.prefix));

    if (tempObj != null)
    {
        if (cached == null)
        {
            cache.Add(x.cld, tempObj);
        }

        x.prefix2 = tempObj.prefix;
        x.country = tempObj.Country;
        x.city = tempObj.City;
    }
    else
    {
        x.prefix2 = "InBound";
        x.country = "Unknown";
        x.city = "Unknown";
    }
}

您可能还想考虑使用Parallel.ForEach而不仅仅是foreach。

答案 1 :(得分:1)

你的问题很难,因为你需要“最接近”的解决方案而不是任何解决方案。这会强制您对listB中{(1}}中每个元素的每条记录进行迭代。

由于您需要listA中每个元素的答案,因此您必须检查其中的每个元素。

但是,您可以通过创建树结构来预处理listA。您为B中所有字符串的每个不同的第一个数字创建一个节点。然后该节点将成为listB中以该数字开头的所有记录的父节点。该节点下面的节点将保留字符串中的第二个数字,依此类推。

走在前面,让你直观地了解这样一棵树的样子:

enter image description here

现在,如果您在listB中进行搜索,则不必遍历整个listB,但可以遍历列表,这会将每次迭代的时间从listB增加到O(n) O(log n)

你会在listA的记录中取第一个字母并将其与树进行比较,然后遍历该分支(立即消除大量您需要比较的记录,从而提高您的表现) 。然后比较第二个字母等,直到在树中找不到更多的字母。停止后,您在listB找到了最长匹配记录,保证“最接近”匹配,FirstOrDefault(\x -> x.StartsWith())根本不匹配! (它只找到第一个匹配,几乎总是只是第一个字母!)。

您只需为listA中的所有搜索创建此树一次,如果listB中有更改,您也可以轻松更新树。

如果您在具有多个核心的合适计算机上运行此程序,则还可以并行化此搜索。它会增加您正在编写的程序的复杂性,因为您需要管理哪个线程搜索listA中的哪个记录,尽管它会大大提高性能并大大降低所需的时间。