如何将IEnumerable<IEnumerable<T>>转换为IEnumerable<T>?

5

我有一个 IEnumerable<IEnumerable<T>> 集合,想把它转换成单维集合。是否可以使用通用扩展方法来实现?目前我正在执行以下操作来实现。

List<string> filteredCombinations = new List<string>();

//For each collection in the combinated results collection
foreach (var combinatedValues in combinatedResults)
{
    List<string> subCombinations = new List<string>();
    //For each value in the combination collection
    foreach (var value in combinatedValues)
    {

        if (value > 0)
        {
            subCombinations.Add(value.ToString());
        }
    }
    if (subCombinations.Count > 0)
    {
       filteredCombinations.Add(String.Join(",",subCombinations.ToArray()));
    }
}

如果无法获得通用解决方案,我该如何以优雅的方式进行优化?

1
标题中的问题在这里得到了回答:https://dev59.com/u3I-5IYBdhLWcg3w99kH - Matt Ellen
5个回答

18
你可以使用Enumerable.SelectMany扩展方法来实现。
如果我正确理解了你的代码,那么代码应该是这样的:
var filteredCombinations = combinatedResults.SelectMany(o => o)
    .Where(value => value > 0)
    .Select(v => v.ToString());

编辑:如评论所述,上面的代码没有将子集的每个元素连接成字符串,与原始代码不同。使用内置方法,您可以使用以下方法完成:

var filteredCombinations = combinatedResults
     .Where(resultSet => resultSet.Any(value => value > 0)
     .Select(resultSet => String.Join(",",
         resultSet.Where(value => value > 0)
                  .Select(v => v.ToString()).ToArray()));

如果您希望每个子集都成为新集合的一个元素,该怎么办?这就是我使用String.Join的方式... - John Doe
@Earwicker,我不明白为什么结果会出现空字符串。resultSet必须从数值类型的可枚举对象开始,因为元素将与零进行比较,这是原始代码中的操作。(我承认我没有使用Visual Studio编写代码,所以请随意纠正我 :-)) - driis
原始代码中有两个 if 语句,而你只有一个 Where 子句。你保留了过滤掉零值的那个,但是你错过了过滤掉空子列表的那个。一个包含所有零的列表将变成一个空数组,然后你将其传递给 Join。原始代码并没有这样做。 - Daniel Earwicker
嗯,现在你正在使用相同的表达式两次查询每个“resultSet”。抱歉挑剔一点……你可以直接复制我的答案以节省时间! :) - Daniel Earwicker

3

我个人会使用Enumerable.SelectMany,就像driis所建议的那样。

然而,如果你想要自己实现这个功能,最好的方式是:

IEnumerable<T> MakeSingleEnumerable<T>(IEnumerable<IEnumerable<T>> combinatedResults)
{
    foreach (var combinatedValues in combinatedResults) {
         foreach (var value in combinatedValues)
              yield return value;
    }
}

3

这里是您需要的内容:

var strings = combinedResults.Select
    (
        c => c.Where(i => i > 0)
        .Select(i => i.ToString())
    ).Where(s => s.Any())
    .Select(s => String.Join(",", s.ToArray());

1
整个问题都很奇怪!措辞问了一个问题,示例又问了另一个问题。而你回答了第三个完全不同的问题,并且被接受了……我已经发布了正确的答案,但没有得到任何投票(虽然我不介意,这很有趣)。 - Daniel Earwicker
我认为你的Implode函数如果只是在IEnumerable<string>类型上操作会更加优雅。如果你想对其他类型序列的每个项目进行转换,请先使用Select函数。 - Daniel Earwicker
如果您的实现使用 string.Join 而不是 StringBuilder,那么它可能会更有效率且更简洁。例如:return string.Join(separator,sequence.ToArray()) - Eamon Nerbonne
@Eamon:经过一番对String.Join实现的深入挖掘,我认为你是完全正确的。已修复。 - Matt Howells

2

您提出了两个不同的问题。您在标题中描述的那个已经被drilis回答了。

但是,您的示例代码是另一个问题。我们可以分阶段重构它。第一步,使用一些Linq构建subCombinations列表:

List<string> filteredCombinations = new List<string>();

//For each collection in the combinated results collection
foreach (var combinatedValues in combinatedResults)
{
    var subCombinations = combinatedValues.Where(v => v > 0)
                                          .Select(v => v.ToString())
                                          .ToList();

    if (subCombinations.Count > 0)
       filteredCombinations.Add(string.Join(",",subCombinations.ToArray()));
}

现在只剩下外层循环,代码如下:
var filteredCombinations = combinatedResults
    .Select(values => values.Where(v => v > 0)
                            .Select(v => v.ToString())
                            .ToArray())
    .Where(a => a.Count > 0)
    .Select(a => string.Join(",", a));

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接