Как мне сделать Ruby-метод Рубиновый метод «Flatten» в C#. Этот метод сглаживает зубчатый массив в одномерный массив.
Например:
s = [ 1, 2, 3 ] #=> [1, 2, 3]
t = [ 4, 5, 6, [7, 8] ] #=> [4, 5, 6, [7, 8]]
a = [ s, t, 9, 10 ] #=> [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
a.flatten #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10





Рекурсивное решение:
IEnumerable Flatten(IEnumerable array)
{
foreach(var item in array)
{
if (item is IEnumerable)
{
foreach(var subitem in Flatten((IEnumerable)item))
{
yield return subitem;
}
}
else
{
yield return item;
}
}
}
РЕДАКТИРОВАТЬ 1:
Джон объясняет в комментариях, почему это не может быть общий метод, посмотрите!
Обновлено еще раз:
Мэтт предложил сделать это методом расширения. Вот и все, просто замените первую строку на:
public static IEnumerable Flatten(this IEnumerable array)
и вы можете использовать это так:
foreach(var item in myArray.Flatten()) { ... }
Моя первоначальная мысль была: "Почему это не универсальное?" - но, конечно, этого не может быть, потому что итеративная версия T очень редко бывает и T. (например, IEnumerable <object> по-прежнему является объектом, но IEnumerable <string> не является строкой.) Это может стоить того. разъясняя это в ответе.
Также я не уверен, что есть какой-либо способ объявить строго типизированный рваный массив на C#? Это должен быть объект [], что означает, что IEnumerable является подходящим типом параметра для этого метода.
Мэтт: Не уверен, что вы имеете в виду под «строго типизированным рваным массивом», но int [] [] и int [,] (для зубчатых и прямоугольных массивов целых чисел соответственно) подойдут.
@Matt: Действительно, я так и думал.
@Jon: Я всегда стараюсь использовать самый простой подход, который сначала работает. По-видимому, это также самый общий: int [] [] и int [,] оба являются IEnumerables. Если вызывающая функция flatten () заранее знает типы, она всегда может привести к ним.
@ Александр: Извините, я не понял. Я считаю, что вы получили правильный ответ, но, возможно, стоит немного пояснить, почему использование универсального метода (например, Flatten <T>) было бы контрпродуктивным. (Вы не можете преобразовать результат в IEnumerable <T> - вам придется использовать Enumerable.Cast <T>.)
@Jon Я имею в виду рваный массив с отдельными значениями, а также вложенные массивы. Какой тип это {1, 2, {3, {4, 5}}, 6}? Некоторые из его элементов являются массивами, а другие - int.
@Alexander Это было бы еще лучше в качестве метода расширения!
Не придираться к мелочам, но разве имена методов не должны начинаться с заглавной буквы "Flatten" vs "Flatten"?
Я бы ответил в комментарии, но мне нужно больше 300 символов.
Решение @Alexander потрясающее, но оно сталкивается с проблемой с массивами строк. Поскольку строка реализует IEnumerable, я думаю, что она вернет каждый символ в каждой строке. Вы можете использовать общий параметр, чтобы сообщить ему, какие именно вещи вы надеетесь вернуть в этих случаях, например:
public static IEnumerable Flatten<T>(IEnumerable e)
{
if (e == null) yield break;
foreach (var item in e)
{
if (item is T)
yield return (T)item;
else if (item is IEnumerable)
{
foreach (var subitem in Flatten<T>((IEnumerable)item))
yield return subitem;
}
else
yield return item;
}
}
Не могли бы вы просто использовать IEnumerable # SelectMany?
Здесь вы имеете дело с зубчатыми массивами (массивом массивов), а не с многомерными.