2014-02-04 2 views
2

У меня есть IEnumerable<object>, который может содержать или не содержать некоторые вложенные коллекции. Например, моя точка может выглядеть примерно так:Сгладить массив объектов, которые могут содержать массивы

[ "foo", 2, [1, 2, 3, 4], "bar" ] 

И я хочу, чтобы сгладить его:

[ "foo", 2, 1, 2, 3, 4, "bar" ] 

Я думал SelectMany должен работать здесь, но не вполне может найти правильная комбинация. Я мог бы это сделать, но я думал, что должно быть более элегантное решение.

+0

Вы это видели: http://blog.mijalko.com/2009/12/flatten-array-of-arrays-using-linq.html –

+0

Вам нужна рекурсия. – SLaks

+0

@MikeCheel: На самом деле я это сделал, но это не совсем то же самое. Это список списков. Это список объектов, некоторые из которых могут быть списками. Я хочу выбрать как объекты, не являющиеся списками, так и содержимое каждого списка. –

ответ

4
IEnumerable<object> source = new object[] { "test", 1, new[] { 1, 2, 3 }, "test" }; 

var result = source .SelectMany(x => x is Array ? ((IEnumerable)x).Cast<object>() : Enumerable.Repeat(x, 1)); 

Чтобы получить его работы с вложенными массивами сделать лямбда-рекурсивных:

IEnumerable<object> source = new object[] { "test", 1, new object[] { 1, 2, new [] { "nested", "nested2" } }, "test" }; 

Func<IEnumerable<object>, IEnumerable<object>> flatten = null; 
flatten = s => s.SelectMany(x => x is Array ? flatten(((IEnumerable)x).Cast<object>()) : Enumerable.Repeat(x, 1)); 

var result = flatten(source); 
+2

Это не будет обрабатывать вложенные массивы. – SLaks

+0

@SLaks Я обновил свой ответ с помощью рекурсивного решения. – MarcinJuraszek

2

Это немного противно, но работает .. если предположить только один уровень:

a.SelectMany(t => 
    (t is IEnumerable<object>) 
     ? (IEnumerable<object>)t 
     : new object[] {t}) 

Я подозреваю, самым «элегантным» решением является на самом деле написать расширение.

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> @this) { 
    foreach (var item in @this) { 
     if (item is IEnumerable<T>) { 
      foreach (var subitem in Flatten((IEnumerable<T>)item)) { 
       yield return subitem; 
      } 
     } 
     else yield return item; 
    } 
} 
+1

Более короткий метод расширения: https://twitter.com/Schabse/status/430799714091409408 – SLaks

+1

Это умно! Тем не менее, я по-прежнему предпочитаю мой для ясности. – Blorgbeard

+0

Да; за какой-то момент краткость и ясность могут быть взаимоисключающими. – SLaks

3

Чуть короче рекурсивный альтернатива:

IEnumerable<object> source = new object[] { "test", 1, new object[] { 1, 2, new [] { "nested", "nested2" } }, "test" }; 

Func<IEnumerable<object>, IEnumerable<object>> flatten = null; 
flatten = s => s == null ? null : s.SelectMany(x => 
    flatten(x as IEnumerable<object>) ?? new [] { x } 
); 
var result = flatten(source); 
Смежные вопросы