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

时间:2018-02-27 13:38:30

标签: c# linq

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 object
  4. 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);
        }
    }
    

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

2 个答案:

答案 0 :(得分:1)

如果您使用在搜索中使用的组合键对两个集合进行分组,即{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)。

答案 1 :(得分:1)

用纯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();
    }
}