【问题标题】:Combining information from multiple lists组合来自多个列表的信息
【发布时间】:2018-05-09 23:35:46
【问题描述】:

我有多个string[],它们的定义如下:

var allEntities = new[] { "a", "b", "c", "d", "e", "f", "g", "h", "i" }
var entitiesWithPriority = new[] { "c", "d", "g" }
var entitiesWithIssue = new [] { "d", "h", "i" }
var entitiesWith... = new [] { ... }

现在我想使用以下类结构从这些数组中编译一个列表:

class Entity {
    public string Name { get; set; }
    public bool HasPriority { get; set; }
    public bool HasIssue { get; set; }
}

因此,操作的结果应该是这样的:

[
    { "name": "a", "hasPriority": false, "hasIssue": false }
    { "name": "b", "hasPriority": false, "hasIssue": false }
    { "name": "c", "hasPriority": true, "hasIssue": false }
    { "name": "d", "hasPriority": true, "hasIssue": true }
    { "name": "e", "hasPriority": false, "hasIssue": false }
    { "name": "f", "hasPriority": false, "hasIssue": false }
    { "name": "g", "hasPriority": true, "hasIssue": false }
    { "name": "h", "hasPriority": false, "hasIssue": true }
    { "name": "i", "hasPriority": false, "hasIssue": true }
]

我可以在下面的代码行中做一些事情,但我认为这个解决方案一点也不完美:

var result = allEntities.Select(entityName => new Entity()
{
    Name = entityName,
    HasPriority = entitiesWithPriority.Contains(entityName),
    HasIssue = entitiesWithIssue.Contains(entityName)
});

如何在提高性能的同时编译这样的列表。

【问题讨论】:

  • “巨大的性能问题”?您最终将获得多少百万个实体?
  • @Blorgbeard 我最终可能会收到大约 10.000 到 100.000 个条目。问题是,每个ContainsAny 调用都会遍历整个列表,所以我正在寻找一个好的解决方案。顺便说一下,上面描述的问题只是一个简化的例子。问题可能不是条目数量,而是调用次数。我生成了大约 10 到 50 个这样的数组组合。

标签: c# arrays performance linq sorting


【解决方案1】:

首先创建一个dictionary <string>,<entity> 并从所有实体中填充它,所有hasXXX 默认为false

之后,查找是 O(1)

foreach (var key in EntitiesWithPriority)
{
   dict[key].hasPriority = true
}

等等

【讨论】:

  • 这似乎是一种时尚的方法。仍然觉得可能有一个完全基于for loops 的解决方案,可能会更快。
  • 好吧 - 也许更好的解决方案是调查您正在构建所有 EntitiesWith 的代码...毕竟,当您创建这些数组时,您可能已经填满了字典...跨度>
  • 遗憾的是,似乎没有简单的解决方法。我已经调查过了。我目前正在使用表达式来获取这些列表。
【解决方案2】:

你可以把他们联合起来:

var entities = 
     from name in allEntities
     join pr in entitiesWithPriority on name equals pr into np
     from priority in np.DefaultIfEmpty()
     join iss in entitiesWithIssue on name equals iss into npi
     from issue in npi.DefaultIfEmpty()
     select new Entity 
     { 
         Name = name, 
         HasPriority = !string.IsNullOrEmpty(priority), 
         HasIssue = !string.IsNullOrEmpty(issue)
     };

【讨论】:

  • 虽然这显然是实现 OP 目标的最简洁方式,但“LEFT JOIN”仍然会在 O(n) 中执行匹配,这对于 large来说可能不够快> 列表。
【解决方案3】:

您可以使用DictionaryHashsetAnyContains 的复杂性从O(n) 降低到O(1)

上面的想法仍然可以用于 large 列表来替换 list/array/IEnumarable Contains 这是 O(n)Hashset 的包含 O(1)

var entitiesWithPriorityMap = entitiesWithPriority.ToDictionary(e => e.entityName, e => true);  
var entitiesWithIssueMap = entitiesWithIssue.ToDictionary(e => e.entityName, e => true);

var result = allEntities.Select(entityName => new Entity()
{
    Name = entityName,
    HasPriority = entitiesWithPriorityMap.ContainsKey(entityName),
    HasIssue = entitiesWithIssueMap.ContainsKey(entityName)
});

注意:这在处理 Linq2Object 或 Linq2Sql 时有效(在这种情况下无法从 Contains 生成 SQL)。

Hashset 版本

var entitiesWithPriorityMap = new HashSet<Entity>(entitiesWithPriority);
var entitiesWithIssueMap = new HashSet<Entity>(entitiesWithIssue);

// replace ContainsKey with Contains

【讨论】:

  • 我已经更新了这个问题,因为我认为你可能误解了我想要达到的目标。
  • @SebastianKrogull - 是的,我现在明白了。但是,这个想法仍然成立,因为您需要更快地进行查找,因为它们针对每个 Select 步骤执行。正常情况下一个Hashset就够了,但是没有Linq扩展可以直接获取Hashset。
  • 只是一个小的附加信息:数组中的项目可以只包含来自 AllEntities 数组的项目。
  • 如果 HashSet 可以,为什么要字典?
  • @Evk - 我使用了字典,因为已经有一个内置的扩展方法来获取它。但是,使用here提供的扩展方法可以轻松获得Hashset
【解决方案4】:

如果构造可以是List&lt;&gt; 而不是array,我们可以创建一个具有以下泛型类型的List&lt;&gt;List&lt;Dictionary&lt;string, dynamic&gt;&gt; 要实现这样的构造,您需要创建以下方法:

public List<List<Dictionary<string, dynamic>>> MethodName()
    {
        var list = new List<List<Dictionary<string, dynamic>>>();

        HasPriority = false;
        HasIssue = false;

        foreach (var item in allEntities)
        {
            if (entitiesWithPriority.Contains(item.ToString()))
                HasPriority = true;
            if (entitiesWithIssue.Contains(item.ToString()))
                HasIssue = true;
            list.Add(new List<Dictionary<string, dynamic>>()
            {
                (new Dictionary<string, dynamic>() { { "name", item } }),
                (new Dictionary<string, dynamic>() { { "hasPriority", HasPriority } }),
                (new Dictionary<string, dynamic>() { { "hasIssue", HasIssue } })
            });
        }

        return list;
    }

我不确定您班级中的所有属性是否都是必需的。如果您想添加更多带有条件的数组,您应该修改 foreach 循环。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    相关资源
    最近更新 更多