按嵌套集合中对象中的属性过滤集合

时间:2021-05-20 11:03:56

标签: c# linq

我有三个班级:

public class Error
{
    public List<ErrorOccurrenceDetails> Occurrences { get; set; }
}
public class ErrorOccurrenceDetails
{
    public Dictionary<DateTime, ErrorTime> Times { get; set; }
}
public class ErrorTime
{
    public string Version { get; set; }
}

有一个 Error 有多个 Occurrences 发生在不同的 Times 中。每次都可以在不同的 Version 上发生。

我有一个 Error 列表。我需要删除没有提供版本的错误,例如如果用户只想看到版本“1.0”中的错误,其他版本(例如“1.1”、“1.2”)需要移除。但是,如果错误出现在版本“1.0”和其他版本上,过滤器应仅删除错误的出现,而不是整个错误。

我尝试这样做:

private IList<Error> errors;

private void Filter(string filterVersion)
{
    var errorsToRemove = new Dictionary<int, ErrorOccurrenceDetails>();
    foreach (Error error in this.errors) 
    {
        int index = this.errors.IndexOf(error);
        var listOfSearchedItems = error.Occurences.ToList();
        listOfSearchedItems.RemoveAll(x => x.Times.Values.Any(y => y.Version != filterVersion));

        var errorOccurences = error.Occurences.Except(listOfSearchedItems).ToList();
        if (errorsToRemove.ContainsKey(index))
        {
            errorsToRemove[index].AddRange(errorOccurences);
        }
        else
        {
            errorsToRemove.Add(index, errorOccurences);
        }
    }
}

我正在尝试将所有需要删除的错误放入 errorsToRemove 字典中,其中 key 是 this.errors 中的错误索引,value 是未在搜索版本中出现的列表。 它有点有效,但有时不会删除坏版本并导致一些正确版本也被删除,所以有一个我没有注意到的漏洞。

此小提琴中的示例错误列表: https://dotnetfiddle.net/Kxq50i

3 个答案:

答案 0 :(得分:1)

根据 Heinzi 的回答,我决定使用更简单的方法。我想我想不惜一切代价避免 Collection was modified; enumeration operation may not execute 异常并开始做一些愚蠢的事情。

private IList<Error> errors;

private static void Filter(string filterVersion)
{
    for (int index = 0; index < errors.Count; index++) 
    {
        Error error = errors[index];
        foreach (var occurrence in error.Occurrences)
        {
            var toRemove = occurrence.Times.Keys.Where(key => occurrence.Times[key].Version != filterVersion).ToList();
            foreach (var key in toRemove)
            {
                occurrence.Times.Remove(key);
            }

            if (occurrence.Times.Count == 0)
            {
                 errors[index] = null; // not deleted to prevent index shift, null is hidden in UI
            }
        }
    }
}

解决方案已更新 in this fiddle

答案 1 :(得分:0)

原因可能是以下几行可能会导致原始 error.Occurences 的项目被删除:

var listOfSearchedItems = error.Occurences.ToList();
listOfSearchedItems.RemoveAll(x => x.Times.Values.Any(y => y.Version != filterVersion));

尝试用下面的替换这两行:

var listOfSearchedItems = error.Occurences.Where(x => x.Times.Values.Any(y => y.Version == filterVersion)); // Note: Here we also replaced the "!=" with "=="

答案 2 :(得分:0)

在使用 Linq 时,更容易考虑包含的内容,而不是您想要删除的内容,然后以这种方式编写代码。例如:

private static IList<Error> Filter(string filterVersion)
{
    var filtered = errors.Where(error => error.Occurrences.Any(occurrence => occurrence.Times.Any(kvp => kvp.Value.Version == filterVersion)))
        .Select(error =>
        {
            return new Error
            {
                Occurrences = error.Occurrences
                    .Where(occurrence => occurrence.Times.Any(kvp => kvp.Value.Version == filterVersion))
                    .Select(occurrence => 
                    {
                        return new ErrorOccurrenceDetails
                        {
                            Times = new Dictionary<DateTime, ErrorTime>(occurrence.Times.Where(kvp => kvp.Value.Version == filterVersion))
                        };
                    })
                    .ToList()
            };
        })
        .ToList();
    return filtered;
}

这种方法的另一个可能的好处是您不会修改原始数据,因此您可以应用不同的过滤器而无需重新阅读。