I have nested tuples like - Tuple<Tuple<Tuple<string,string>, string>, string>
.
I want to flatten them like Tuple<string,string,string,string>
. I see that is can be done in f#. Is there a version of - F# flatten nested tuples - in c#
Asked
Active
Viewed 1,126 times
2

Milan
- 119
- 2
- 12
-
3I think rather than flattening them, you should consider redesigning this. Horrific, with all due respect. – JuanR Mar 11 '19 at 18:47
-
1`var flattened = new Tuple
(t.Item1.Item1.Item1, t.Item1.Item1.Item2, t.Item1.Item2, t.Item2);` – Rufus L Mar 11 '19 at 18:54 -
Note that the F# code in your link does not do what you claim. It flattens nested tuples into a list. Is that what you want or do you want it turned into another tuple? – Mike Zboray Mar 11 '19 at 18:59
-
1@JuanR while I totally agree with you, this what I have to live with a legacy library :( – Milan Mar 11 '19 at 20:13
-
@Milan: Sorry to hear that. Well, you are in luck sir for I have provided an answer to your problems below! – JuanR Mar 11 '19 at 20:17
3 Answers
3
Assuming you insist in using this rather obnoxious design, here is how you could do it:
/// <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;
}
Sample usage:
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);
Output:
(value1, value2, value2b, value2c)
Sample 2 (integers):
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);
Output:
(1, 2, 3, 4)

JuanR
- 7,405
- 1
- 19
- 30
-
2 important notes on this. Make sure you use the same type and make sure you have no more than 6 elements. – Franck Mar 11 '19 at 19:30
-
Thank you @Franck. Yes, the assumption is the type of values is the same. Otherwise you can use `object` as the type. I believe the limitation for elements is 8. Then again, if you nest another tuple for element number 8, you essentially get unlimited tuple size since we are unpacking them anyways. – JuanR Mar 11 '19 at 19:33
-
@JuanR This is great answer. I find Aleks great as well. Thanks a lot for taking time to answer this! – Milan Mar 11 '19 at 20:23
2
You can convert your Tuple
to the flat list with recursion and Deep-First Search. Try this method:
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;
}
}
}
You can use it like this:
var input = Tuple.Create(Tuple.Create(Tuple.Create("a0", "a1"), "a2"), "b", "c");
var items = DFS(input).ToArray();
// items[2] would be "a2"
Please note that reflection may slow down your app so try to avoid it while it is possible

Aleks Andreev
- 7,016
- 8
- 29
- 37
-1
If you have a single item then create a converter which is easier to manage especially if you have a List
eventually.
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
);
}
Please note that Tuple
is not meant to be modify along the way. If you do need it, it mean you actually have a very bad design. Best solution remain to rethink how things work.

Franck
- 4,438
- 1
- 28
- 55