找到匹配元素时从列表中减去值

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了找到匹配元素时从列表中减去值相关的知识,希望对你有一定的参考价值。

public class ScenarioResults
{
     public string ProductSubType { get; set; }

     public string SubBook { get; set; }

     public decimal? ScenarioValue { get; set; }
}

我有两个“ScenarioResults”对象列表。我需要获得一个新列表,其中的值与两个列表中的scenariovalue不同。

减法标准:

  1. 如果productsubtype,子书匹配两个列表中的对象,则减去两者中的scenariovalue。
  2. 如果在第一个列表中找不到匹配,则获取第一个列表对象
  3. 如果在第二个列表中找不到匹配,则获取-second list对象

StartResults和EndResults是我拥有的两个列表。我将结果添加到结果列表中我编写了以下代码并且它有效:

var results = new List<ScenarioResults>();
foreach (var endResult in resultsEnd)
{
    var result = ScenarioResults.Clone(endResult);
    var startResult = resultsStart.FirstOrDefault(x.SubBook == result.SubBook && x.ProductSubType == result.ProductSubType);

    if (startResult == null)
    {
        result.ScenarioValue = endResult.ScenarioValue;
        results.Add(result);
    }
    else
    {
        result.ScenarioValue = endResult.ScenarioValue - startResult.ScenarioValue;
        results.Add(result);
    }
}
foreach (var startResult in resultsStart)
{
    var result = ScenarioResults.Clone(startResult);
    var endResult = resultsEnd.Any(x.SubBook == result.SubBook && x.ProductSubType == result.ProductSubType);
    if (!endResult)
    {
        result.ScenarioValue = -startResult.ScenarioValue;
        results.Add(result);
    }
}

有没有更好的方法来解决这个问题?如何提高这个性能?

答案

如果您使用在搜索中使用的组合键对两个集合进行分组,即{SubBook, ProductSubType},则可以简化和加快代码

var startDict = resultsStart
    .GroupBy(r => new {r.SubBook, r.ProductSubType})
    .ToDictionary(g => g.Key, g => g.First());
var endDict = resultsEnd
    .GroupBy(r => new {r.SubBook, r.ProductSubType})
    .ToDictionary(g => g.Key, g => g.First());
foreach (var key in startDict.Keys.Union(endDict.Keys)) {
    var hasStart = startDict.TryGetValue(key, out var start);
    var hasEnd = endDict.TryGetValue(key, out var end);
    if (hasStart && hasEnd) {
        ... // Construct the difference with subtraction
    } else if (hasStart) {
        ... // Construct the difference where only the start is present
    } else if (hasEnd) {
        ... // Construct the difference where only the end is present
    }
}

由于哈希查找,该解决方案是O(S + E),而原始解决方案是O(S * E),因为嵌套线性搜索。

另一答案

用纯LINQ :)。这只是一个展示可能必须稍微调整但仍然可以做到这一点。只是为了表明一个想法。

List<ScenarioResults> a = new List<ScenarioResults> { new ScenarioResults { ProductSubType = "1", SubBook = "1", ScenarioValue = 10}, new ScenarioResults { ProductSubType = "2", SubBook = "2", ScenarioValue = 10 } };
List<ScenarioResults> b = new List<ScenarioResults> { new ScenarioResults { ProductSubType = "2", SubBook = "2", ScenarioValue = 10 }, new ScenarioResults { ProductSubType = "3", SubBook = "3", ScenarioValue = 10 } };

List<ScenarioResults> result = a.Concat(b.Select(_ => new ScenarioResults { ProductSubType = _.ProductSubType, SubBook = _.SubBook, ScenarioValue = -_.ScenarioValue ?? 0}))
    .GroupBy(x => new {x.ProductSubType, x.SubBook})                  
    .SelectMany(g => g.Select(_ => new ScenarioResults { ProductSubType = _.ProductSubType, SubBook = _.SubBook, ScenarioValue = (g.First().ScenarioValue ?? 0) + g.Skip(1).Sum(v => v.ScenarioValue)}))
    .Distinct(new EqualityComparer())
    .ToList();

其中EqualityComparer是这样的:

public class EqualityComparer : IEqualityComparer<ScenarioResults>
{
    public bool Equals(ScenarioResults x, ScenarioResults y)
    {
        return x.ProductSubType == y.ProductSubType && x.SubBook == y.SubBook;
    }

    public int GetHashCode(ScenarioResults obj)
    {
        return obj.ProductSubType.GetHashCode() ^ obj.SubBook.GetHashCode();
    }
}

以上是关于找到匹配元素时从列表中减去值的主要内容,如果未能解决你的问题,请参考以下文章

在最接近指定日期的列表中查找上一个日期

查找是不是在列表中找到具有特定属性值的元素

在片段中创建自定义列表视图时出错。必需的活动,找到的片段

在片段中创建自定义列表视图时出错所需活动,找到片段

如何在单击 RecyclerView 项目时从一个片段移动到另一个片段

13 个非常有用的 Python 代码片段