【问题标题】:Reduce the number of common list property comparison in .NET Core减少 .NET Core 中常用列表属性比较的数量
【发布时间】:2020-07-31 13:46:48
【问题描述】:

我有两个具有一对多关系的表,在此示例中分别由 Customers(one) 和 CustomersInfo(many) 表示。

using System.Collections.Generic;

namespace Domain.Entities
{
    public class CustomersAggregate
    {
        public CustomersAggregate(int id, string name)
        {
            Id = id;
            Name = name;
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public IEnumerable<CustomersInfo> CustomersInfo { get; set; }
    }

    public class CustomersInfo
    {
        public CustomersInfo(int customerId, string email)
        {
            CustomerId = customerId;
            Email = email;
        }

        public CustomersInfo(int customerId, string email, string name)
        {
            CustomerId = customerId;
            Email = email;
            Name = name;
        }

        public int CustomerId { get; set; }
        public string Email { get; set; }
        public string Name { get; set; }
        public CustomersAggregate Customers { get; set; }
    }


}

在实际场景中,我必须读取包含两列的 .xlsx 文件:Name 和 Email。要插入到客户表中,我按名称分组,然后选择每个分组的第一个,然后插入到客户表中。因此,在客户表中,所有名称都是不同的。之后,我再次读取 .xlsx 文件,并将已插入客户中的名称与文件名称列中读取的值进行比较。如果CustomersInfo.CustomerId 相同,则接收Customers.Id。下面我代表描述的:

using Domain.Entities;
using System;
using System.Collections.Generic;

namespace Domain
{
    public static class Program
    {
        public static void Main()
        {
            IEnumerable<Customers> customers = new List<Customers>() {
                new Customers(1, "Gustavo"),
                new Customers(2, "Lilian"),
                new Customers(3, "Ruan"),
                new Customers(4, "Zeze"),
                new Customers(5, "Augusto"),
                new Customers(6, "Maicon"),
                new Customers(7, "Nadia")
            };

            IEnumerable<CustomersInfo> customersInfos = new List<CustomersInfo>()
            {
                new CustomersInfo(0, "gustavo@costa.com", "Gustavo"),
                new CustomersInfo(0, "gustavo@mcosta.com", "Gustavo"),
                new CustomersInfo(0, "lilian@costa.com", "Lilian"),
                new CustomersInfo(0, "lian@costa.com", "Lilian"),
                new CustomersInfo(0, "ruan@co3sta.com", "Ruan"),
                new CustomersInfo(0, "ruan@c2osta.com", "Ruan"),
                new CustomersInfo(0, "ruan@1costa.com", "Ruan"),
                new CustomersInfo(0, "zeze@costa.com", "Zeze"),
                new CustomersInfo(0, "zeze@sscosta.com", "Zeze"),
                new CustomersInfo(0, "austo@lentini.com", "Augusto"),
                new CustomersInfo(0, "austo@lib.com", "Augusto"),
                new CustomersInfo(0, "maicon@22coa.com", "Maicon"),
                new CustomersInfo(0, "nadia@22c.com", "Nadia"),
            };

            var ci = new List<CustomersInfo>();

            foreach (var customer in customers)
            {
                foreach (var customerInfo in customersInfos)
                {
                    if (customer.Name == customerInfo.Name)
                    {
                        ci.Add(new CustomersInfo(customer.Id, customerInfo.Email));
                    }
                }
            }

            ci.ForEach(x =>
            {
                Console.WriteLine(string.Concat(x.CustomerId, " - ", x.Email));
            });
        }
    }
}

问题是 .xlsx 文件有大约 7000 条记录。分组选择不同客户的名字后,Customers表有3000行,然后与文件中的7000条记录进行比较,所以最后需要进行3000 x 7000的比较,这显然使程序非常慢的。我认为提高绩效的一种方法是按字母顺序对客户进行排序,参考客户的中心项目将他们分成两个列表,并仅在这些列表中进行比较。理想情况下,在下面的比较中,如果 customerInfo.Name 的前导字母不是 customerInfo 的首字母,则不会将 customerInfo.Name 与 customer.Name 进行比较。

if (customer.Name == customerInfo.Name)

但是,我不知道如何实现这一点。有人可以帮忙吗?

【问题讨论】:

标签: c# .net-core ef-core-3.1


【解决方案1】:

你可以使用LINQ的方法GroupBy和ToDictionary:

var dict = customersInfos
    .GroupBy(ci => ci.Name)
    .ToDictionary(g => g.Key);
foreach (var customer in customers)
{
    if(dict.ContainsKey(customer.Name))
    {
         ci.AddRange(dict[customer.Name].Select(cInf => new CustomersInfo(customer.Id, cInf.Email)));
    }
}

这应该将操作数从原来的 O(customer.Lenth * customersInfos.Length) 减少到 O(customer.Lenth + customersInfos.Length)

【讨论】:

    【解决方案2】:

    尝试通过找出瓶颈来使用算法方法。瓶颈在于,对于每个customer 整个customerInfos 列表进行扫描以确保是否有email 匹配。它需要 O(n)。

    通过customer.Name 查找email 的操作可以使用Dictionary&lt;string,string&gt; 数据结构来完成。通过customer.Name搜索需要O(1)

    var dict = customerInfos.ToDictionary(x => x.Email, x => x.Name);
    foreach (var customer in customers)
        if (dict.ContainsKey(customer.Name))
            ci.Add(new CustomersInfo(customer.Id, dict[customer.Name]));
    

    【讨论】:

    • 这将抛出 OP 的数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-05
    • 2016-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-06-15
    相关资源
    最近更新 更多