如何在C#中扁平化嵌套元组?

时间:2019-03-11 18:40:18

标签: c# tuples

我有嵌套的元组,例如-Tuple<Tuple<Tuple<string,string>, string>, string>。 我想像Tuple<string,string,string,string>那样压扁它们。我看到可以在f#中完成。在C#中是否有-F# flatten nested tuples-的版本

3 个答案:

答案 0 :(得分:1)

假设您坚持使用这种相当讨厌的设计,那么您可以按照以下方法进行操作:

/// <summary>
/// Constructs a tuple our of an array of arguments
/// </summary>
/// <typeparam name="T">The type of argument.</typeparam>
/// <param name="values">The values.</param>
/// <returns></returns>
public static object ConstructTuple<T>(params T[] values)
{
    Type genericType = Type.GetType("System.Tuple`" + values.Length);
    Type[] typeArgs = values.Select(_ => typeof(T)).ToArray();
    Type specificType = genericType.MakeGenericType(typeArgs);
    object[] constructorArguments = values.Cast<object>().ToArray();
    return Activator.CreateInstance(specificType, constructorArguments);
}

/// <summary>
/// Flattens a tupple into an enumeration using reflection.
/// </summary>
/// <typeparam name="T">The type of objects the nested tuple contains.</typeparam>
/// <param name="tuple">The tuple to flatten.</param>
/// <returns></returns>
public static IEnumerable<T> FlattenTupple<T>(object tuple)
{            
    List<T> items = new List<T>();
    var type = tuple.GetType();

    if (type.GetInterface("ITuple") == null)
        throw new ArgumentException("This is not a tuple!");

    foreach (var property in type.GetProperties())
    {
        var value = property.GetValue(tuple);
        if (property.PropertyType.GetInterface("ITuple") != null)
        {                    
            var subItems = FlattenTupple<T>(value);
            items.AddRange(subItems);
        }
        else
        {
            items.Add((T)value);
        }
    }
    return items;
}

样品用量:

Tuple<Tuple<Tuple<string, string>, string>, string> tuple =
            new Tuple<Tuple<Tuple<string, string>, string>, string>(new Tuple<Tuple<string, string>, string>(new Tuple<string, string>("value1", "value2"), "value2b"), "value2c");

var items = FlattenTupple<string>(tuple);
var flattened = ConstructTuple(items.ToArray());
Console.WriteLine(flattened);

输出:

  

(值1,值2,值2b,值2c)

样本2(整数):

Tuple<Tuple<Tuple<int, int>, int>, int> intTuple =
            new Tuple<Tuple<Tuple<int, int>, int>, int>(new Tuple<Tuple<int, int>, int>(new Tuple<int, int>(1, 2), 3), 4);

var intItems = FlattenTupple<int>(intTuple);
var flattened2 = ConstructTuple(intItems.ToArray());
Console.WriteLine(flattened2);

输出:

  

(1、2、3、4)

答案 1 :(得分:1)

您可以使用递归和深度优先搜索将Tuple转换为平面列表。试试这个方法:

public static IEnumerable<object> DFS(object t)
{
    var type = t.GetType();
    if (type.FullName?.StartsWith("System.Tuple") != true) // or check inheritanse from ITuple
        yield return t;

    var items = type.GetProperties()
        .Where(p => p.Name.StartsWith("Item"))
        .Select(p => p.GetValue(t))
        .ToArray();
    foreach (var item in items)
    {
        foreach (var innerItem in DFS(item))
        {
            yield return innerItem;
        }
    }
}

您可以像这样使用它:

var input = Tuple.Create(Tuple.Create(Tuple.Create("a0", "a1"), "a2"), "b", "c");
var items = DFS(input).ToArray();
// items[2] would be "a2"

请注意,反射可能会使您的应用变慢,因此请尽可能避免使用它

答案 2 :(得分:-1)

如果您只有一个项目,则创建一个转换器,该转换器更易于管理,尤其是如果您最终拥有List的话。

public Tuple<string, string, string, string> ConvertSomething(Tuple<Tuple<Tuple<string,string>, string>, string> original)
{
    return new Tuple<string, string, string, string>
               (
                   original.Item1.Item1.Item1, 
                   original.Item1.Item1.Item2,
                   original.Item1.Item2,
                   original.Item2
               );
}

请注意,Tuple并非一路修改。如果确实需要它,则意味着您实际上有一个非常糟糕的设计。最好的解决方案仍然是重新考虑事物的工作方式。