How to easily initialize a list of Tuples?

354,309

Solution 1

c# 7.0 lets you do this:

  var tupleList = new List<(int, string)>
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

If you don't need a List, but just an array, you can do:

  var tupleList = new(int, string)[]
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

And if you don't like "Item1" and "Item2", you can do:

  var tupleList = new List<(int Index, string Name)>
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

or for an array:

  var tupleList = new (int Index, string Name)[]
  {
      (1, "cow"),
      (5, "chickens"),
      (1, "airplane")
  };

which lets you do: tupleList[0].Index and tupleList[0].Name

Framework 4.6.2 and below

You must install System.ValueTuple from the Nuget Package Manager.

Framework 4.7 and above

It is built into the framework. Do not install System.ValueTuple. In fact, remove it and delete it from the bin directory.

note: In real life, I wouldn't be able to choose between cow, chickens or airplane. I would be really torn.

Solution 2

Yes! This is possible.

The { } syntax of the collection initializer works on any IEnumerable type which has an Add method with the correct amount of arguments. Without bothering how that works under the covers, that means you can simply extend from List<T>, add a custom Add method to initialize your T, and you are done!

public class TupleList<T1, T2> : List<Tuple<T1, T2>>
{
    public void Add( T1 item, T2 item2 )
    {
        Add( new Tuple<T1, T2>( item, item2 ) );
    }
}

This allows you to do the following:

var groceryList = new TupleList<int, string>
{
    { 1, "kiwi" },
    { 5, "apples" },
    { 3, "potatoes" },
    { 1, "tomato" }
};

Solution 3

C# 6 adds a new feature just for this: extension Add methods. This has always been possible for VB.net but is now available in C#.

Now you don't have to add Add() methods to your classes directly, you can implement them as extension methods. When extending any enumerable type with an Add() method, you'll be able to use it in collection initializer expressions. So you don't have to derive from lists explicitly anymore (as mentioned in another answer), you can simply extend it.

public static class TupleListExtensions
{
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
            T1 item1, T2 item2)
    {
        list.Add(Tuple.Create(item1, item2));
    }

    public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
            T1 item1, T2 item2, T3 item3)
    {
        list.Add(Tuple.Create(item1, item2, item3));
    }

    // and so on...
}

This will allow you to do this on any class that implements IList<>:

var numbers = new List<Tuple<int, string>>
{
    { 1, "one" },
    { 2, "two" },
    { 3, "three" },
    { 4, "four" },
    { 5, "five" },
};
var points = new ObservableCollection<Tuple<double, double, double>>
{
    { 0, 0, 0 },
    { 1, 2, 3 },
    { -4, -2, 42 },
};

Of course you're not restricted to extending collections of tuples, it can be for collections of any specific type you want the special syntax for.

public static class BigIntegerListExtensions
{
    public static void Add(this IList<BigInteger> list,
        params byte[] value)
    {
        list.Add(new BigInteger(value));
    }

    public static void Add(this IList<BigInteger> list,
        string value)
    {
        list.Add(BigInteger.Parse(value));
    }
}

var bigNumbers = new List<BigInteger>
{
    new BigInteger(1), // constructor BigInteger(int)
    2222222222L,       // implicit operator BigInteger(long)
    3333333333UL,      // implicit operator BigInteger(ulong)
    { 4, 4, 4, 4, 4, 4, 4, 4 },               // extension Add(byte[])
    "55555555555555555555555555555555555555", // extension Add(string)
};

C# 7 will be adding in support for tuples built into the language, though they will be of a different type (System.ValueTuple instead). So to it would be good to add overloads for value tuples so you have the option to use them as well. Unfortunately, there are no implicit conversions defined between the two.

public static class ValueTupleListExtensions
{
    public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
        ValueTuple<T1, T2> item) => list.Add(item.ToTuple());
}

This way the list initialization will look even nicer.

var points = new List<Tuple<int, int, int>>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};

But instead of going through all this trouble, it might just be better to switch to using ValueTuple exclusively.

var points = new List<(int, int, int)>
{
    (0, 0, 0),
    (1, 2, 3),
    (-1, 12, -73),
};

Solution 4

You can do this by calling the constructor each time with is slightly better

var tupleList = new List<Tuple<int, string>>
{
    new Tuple<int, string>(1, "cow" ),
    new Tuple<int, string>( 5, "chickens" ),
    new Tuple<int, string>( 1, "airplane" )
};

Solution 5

Old question, but this is what I typically do to make things a bit more readable:

Func<int, string, Tuple<int, string>> tc = Tuple.Create;

var tupleList = new List<Tuple<int, string>>
{
    tc( 1, "cow" ),
    tc( 5, "chickens" ),
    tc( 1, "airplane" )
};
Share:
354,309
Steven Jeuris
Author by

Steven Jeuris

I have a PhD in Human-Computer Interaction and am currently working both as a software engineer at iMotions and as a postdoc at the Technical University of Denmark (DTU). This blend of research and development is the type of work which motivates and excites me the most. Currently, I am working on a distributed platform which enables researchers to conduct biometric research 'in the wild' (outside of the lab environment). As part of my previous research, I developed a fully functional system (called Laevo), exploring an alternative to the prevalent, antiquated, desktop computing paradigm. This work (and related publications) contributes to a line of research called 'activity-centric computing'. I have almost 10 years of professional software development experience. Prior to academia, I worked for several years as a professional full-stack software developer at a game development company in Belgium: AIM Productions. I immediately started working here after a successful internship concluding my bachelor degree (obtained in 2007). My job entailed working on interactive hometrainer software and other multimedia applications. This spiked my interest in user interface development. I liked the work and colleagues at the company too much to give up entirely for further studies, so I decided to combine the two. In 2009 I started studying for my master in Game and Media Technology at the University of Utrecht in the Netherlands, from which I graduated in 2012.

Updated on February 11, 2021

Comments

  • Steven Jeuris
    Steven Jeuris over 3 years

    I love tuples. They allow you to quickly group relevant information together without having to write a struct or class for it. This is very useful while refactoring very localized code.

    Initializing a list of them however seems a bit redundant.

    var tupleList = new List<Tuple<int, string>>
    {
        Tuple.Create( 1, "cow" ),
        Tuple.Create( 5, "chickens" ),
        Tuple.Create( 1, "airplane" )
    };
    

    Isn't there a better way? I would love a solution along the lines of the Dictionary initializer.

    Dictionary<int, string> students = new Dictionary<int, string>()
    {
        { 111, "bleh" },
        { 112, "bloeh" },
        { 113, "blah" }
    };
    

    Can't we use a similar syntax?

  • goodeye
    goodeye over 11 years
    A downside is having to write the class. Like you, I love tuples because I don't have to write a class or struct.
  • onedaywhen
    onedaywhen over 11 years
    I couldn't get the original code to work, so I've amended it to what I think it should be... which may reverse your opinion on whether the syntax "is slightly better" after all :)
  • Steven Jeuris
    Steven Jeuris almost 9 years
    @BillW Not too certain this is the exact post, ... but I know Jon Skeet has written about it before.
  • Andreas Reiff
    Andreas Reiff over 8 years
    How do you get the value/name syntax with a Tuple?
  • cthepenier
    cthepenier over 8 years
    Compiler tells that anonymous type are not implicitly convertible to Tuple
  • Dave Cousineau
    Dave Cousineau about 8 years
    at least use Tuple.Create instead and you can infer the type arguments
  • Byyo
    Byyo about 8 years
    does this work groceryList[0] == groceryList[1] or does a comparison have to be implemented?
  • Steven Jeuris
    Steven Jeuris over 7 years
    I finally took a decent look at this while updating my library to C# 6.0. Although it looks good at a glance, I prefer my previously posted solution over this because I find TupleList<int, string>. to be more readable than List<Tuple<int, string>>.
  • Jeff Mercado
    Jeff Mercado over 7 years
    That's something that a type alias can fix. Granted the alias itself cannot be generic which may be preferable. using TupleList = System.Collections.Generic.List<System.Tuple<int, string>>;
  • Suraj
    Suraj over 7 years
    I created a Nuget Package with Add methods on ICollection to save others the time of having to maintain this code. See here for package: nuget.org/packages/Naos.Recipes.TupleInitializers See here for code: github.com/NaosProject/Naos.Recipes/blob/master/… This will include a cs file in your solution under a ".Naos.Recipes" folder, so you don't have to drag-around an assembly dependency
  • Suraj
    Suraj over 7 years
    I created a Nuget Package with Add methods on ICollection to save others the time of having to maintain this code. See here for package: nuget.org/packages/Naos.Recipes.TupleInitializers See here for code: github.com/NaosProject/Naos.Recipes/blob/master/… This will include a cs file in your solution under a ".Naos.Recipes" folder, so you don't have to drag-around an assembly dependency
  • Steven Jeuris
    Steven Jeuris over 6 years
    That comes more down to taste whether you prefer to mention types explicitly or not, in the same vain as to use var or not. I personally prefer explicit typing (when it is not duplicated in e.g. the constructor).
  • Алекса Јевтић
    Алекса Јевтић about 6 years
    Can this be used on a .net core 2.0 ?
  • toddmo
    toddmo about 6 years
    @АлексаЈевтић, System.ValueTuple supports core 2.0. But, try it without the Nuget first, as unnecessary packages can cause problems. So one way or another, yes. Use c# v7 or greater if possible.
  • Ed Bayiates
    Ed Bayiates almost 6 years
    Works on old versions of .NET, too!
  • Steven Jeuris
    Steven Jeuris over 5 years
    You make me feel old. :) I might be missing something here, but initializing 'holds' is not concise at all. This was the exact point of the question.
  • djangojazz
    djangojazz over 5 years
    @Steven Jeuris No, you were right I copied and pasted the wrong bit of code for Point 1. Need to go a little slower before I hit 'submit'.
  • Vitox
    Vitox over 4 years
    Absolutely perfect answer! Thanks!
  • pmcnamee
    pmcnamee almost 3 years
    Here's a relevant microsoft link about the collection initializers mentioned in this answer.
  • Rod Talingting
    Rod Talingting over 2 years
    This is what I was looking for. Thanks @onedaywhen