Is it possible to implement a recursive “SelectMany”?

前端 未结 2 1386
天命终不由人
天命终不由人 2020-12-18 23:24

As we all know, Enumerable.SelectMany flattens a sequence of sequences into a single sequence. What if we wanted a method that could flatten sequences of sequen

相关标签:
2条回答
  • 2020-12-19 00:09

    This is trivial in F# with recursive sequence expressions.

    let rec flatten (items: IEnumerable) =
      seq {
        for x in items do
          match x with
          | :? 'T as v -> yield v
          | :? IEnumerable as e -> yield! flatten e
          | _ -> failwithf "Expected IEnumerable or %A" typeof<'T>
      }
    

    A test:

    // forces 'T list to obj list
    let (!) (l: obj list) = l
    let y = ![["1";"2"];"3";[!["4";["5"];["6"]];["7"]];"8"]
    let z : string list = flatten y |> Seq.toList
    // val z : string list = ["1"; "2"; "3"; "4"; "5"; "6"; "7"; "8"]
    
    0 讨论(0)
  • 2020-12-19 00:15

    As far as I understood your idea, this is my variant:

    static IEnumerable<T> Flatten<T>(IEnumerable collection)
    {
        foreach (var o in collection)
        {
            if (o is IEnumerable && !(o is T))
            {
                foreach (T t in Flatten<T>((IEnumerable)o))
                    yield return t;
            }
            else
                yield return (T)o;
        }
    }
    

    and check it

    List<object> s = new List<object>
        {
            "1",
            new string[] {"2","3"},
            "4",
            new object[] {new string[] {"5","6"},new string[] {"7","8"},},
        };
    var fs = Flatten<string>(s);
    foreach (string str in fs)
        Console.WriteLine(str);
    Console.ReadLine();
    

    Obviously, it does lack some type validity checks (an InvalidCastExcpetion if collection contains not T, and probably some other drawbacks)...well, at least it's lazy-evaluated, as desired.

    !(o is T) was added to prevent flattenning of string to char array

    0 讨论(0)
提交回复
热议问题