タプルのリストを簡単に初期化する方法は?


286

タプル大好きです。これらを使用すると、構造体やクラスを記述しなくても、関連情報をすばやくグループ化できます。これは、非常にローカライズされたコードをリファクタリングするときに非常に役立ちます。

ただし、それらのリストの初期化は少し冗長に思われます。

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

もっと良い方法はありませんか?辞書イニシャライザに沿った解決策が欲しいです。

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

同様の構文を使用できませんか?


2
この場合、タプルのリストの代わりに辞書を使用しないのはなぜですか?
エドS.

17
@ Ed S .: A Dictionaryは重複するキーを許可しません。
スティーブンジュリス

2
@EdS .:常に、1つのアイテムがハッシュ可能/順序付け可能で一意である2つのタプルではありません。

良い点は、重複するキーに気づかなかったことです。
エドS.

回答:


279

c#7.0ではこれを行うことができます:

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

を必要とせずList、配列だけが必要な場合は、次のようにできます。

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

また、「Item1」と「Item2」が気に入らない場合は、次のようにできます。

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

または配列の場合:

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

これはあなたが行うことができます:tupleList[0].IndextupleList[0].Name

フレームワーク4.6.2以下

System.ValueTupleNuget Package Managerからインストールする必要があります。

フレームワーク4.7以降

フレームワークに組み込まれています。インストールしないでくださいSystem.ValueTuple。実際には、それ削除してbinディレクトリから削除してください。

注:実際には、牛、鶏、飛行機のいずれかを選択することはできません。本当に引き裂かれます。


2
これは.netコア2.0で使用できますか?
АлексаЈевтић

2
@АлексаЈевтић、System.ValueTupleコア2.0をサポートします。ただし、不要なパッケージが問題を引き起こす可能性があるため、最初にNugetなしで試してください。はい、そうです。可能であれば、c#v7以降を使用してください。
toddmo 2018

1
絶対に完璧な答えです!ありがとう!
Vitox

224

はい!これは可能です。

コレクション初期化子の{}構文は、正しい数の引数 を持つAddメソッドを持つすべてのIEnumerableタイプで機能します。それが内部でどのように機能するかを気にすることなく、つまり、単純にList <T>から拡張し、カスタムのAddメソッドを追加してTを初期化 すれば完了です。

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

これにより、次のことが可能になります。

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

39
欠点は、クラスを記述する必要があることです。あなたと同じように、クラスや構造体を記述する必要がないため、タプルが大好きです。
goodeye 2012年

2
@BillW確かにこれが正確な投稿かどうかはわかりませんが、Jon Skeetが以前にそれについて書いたことは知っています。
Steven Jeuris 2015

1
これは機能しますgroceryList[0] == groceryList[1]か、それとも比較を実装する必要がありますか?
Byyo 2016

ICollectionでAddメソッドを使用してNugetパッケージを作成し、このコードを維持する時間を他の人に節約しました。パッケージについてはこちらをご覧ください:nuget.org/packages/Naos.Recipes.TupleInitializersコードについてはこちらをご覧ください:github.com/NaosProject/Naos.Recipes/blob/master/… これにより、ソリューションの ".Naos .Recipes "フォルダーなので、アセンブリの依存関係をドラッグする必要はありません
SFun28

83

C#6は、このための新機能を追加します。拡張Addメソッドです。これは常にVB.netで可能でしたが、C#で利用できるようになりました。

これでAdd()、クラスにメソッドを直接追加する必要がなくなり、拡張メソッドとして実装できます。Add()メソッドを使用して列挙可能な型を拡張すると、コレクション初期化式でその型を使用できるようになります。そのため、リストから明示的に派生する必要はもうありません(別の回答で述べられているように)、単純に拡張できます。

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...
}

これにより、を実装するすべてのクラスでこれを行うことができます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 },
};

もちろん、タプルのコレクションを拡張することに限定されず、特別な構文が必要な特定のタイプのコレクションを対象とすることができます。

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は、言語に組み込まれたタプルのサポートを追加しますが、タイプは異なります(System.ValueTuple代わりに)。そのため、値タプルにオーバーロードを追加して、それらを使用するオプションを追加することをお勧めします。残念ながら、2つの間に定義された暗黙の変換はありません。

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

これにより、リストの初期化がさらに見やすくなります。

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

ただし、このような問題をすべて解決するのではなく、ValueTuple独占的に使用するように切り替える方がよい場合があります。

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

ライブラリをC#6.0に更新しているときに、ようやくこれを適切に確認しました。一見見栄えは良いですが、以前に投稿した解決策TupleList<int, string>.よりも読みやすくなっているので、こちらの方が好みですList<Tuple<int, string>>
Steven Jeuris

1
これは、型エイリアスが修正できるものです。エイリアス自体が一般的であることは認められませんが、これは望ましい場合があります。using TupleList = System.Collections.Generic.List<System.Tuple<int, string>>;
ジェフメルカド2016年

ICollectionでAddメソッドを使用してNugetパッケージを作成し、このコードを維持する時間を他の人に節約しました。パッケージについてはこちらをご覧ください:nuget.org/packages/Naos.Recipes.TupleInitializersコードについてはこちらをご覧ください:github.com/NaosProject/Naos.Recipes/blob/master/…これにより、ソリューションの ".Naos .Recipes "フォルダー。アセンブリの依存関係をドラッグする必要はありません
SFun28

42

あなたは毎回コンストラクタを呼び出すことでこれを行うことができます

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" )
};

6
元のコードを機能させることができなかったので、本来あるべきコードに修正しました...結局、構文が「やや優れている」かどうかについての意見を逆転させるかもしれません:)
onedaywhen

5
少なくともTuple.Create代わりに使用して、型引数を推測できます
Dave Cousineau

28

古い質問ですが、これは私が物事を少し読みやすくするために通常行うことです:

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" )
};

古いバージョンの.NETでも動作します。
Ed Bayiates、

1

Super Duper Old知っていますが、C#7を使用して、Linqと継続ラムダをメソッドに追加します。クラスで再利用する場合は、DTOと匿名プロジェクションの代わりに名前付きタプルを使用します。はい、モックとテストにはクラスが必要ですが、インラインでクラス内でやり取りすることは、この新しいオプションIMHOを使用するのに適しています。あなたはそれらをインスタンス化することができます

  1. 直接インスタンス化
var items = new List<(int Id, string Name)> { (1, "Me"), (2, "You")};
  1. 既存のコレクションから、匿名の射影が行われていた方法と同様に、よく型付けされたタプルを返すことができます。
public class Hold
{
    public int Id { get; set; }
    public string Name { get; set; }
}

//In some method or main console app:
var holds = new List<Hold> { new Hold { Id = 1, Name = "Me" }, new Hold { Id = 2, Name = "You" } };
var anonymousProjections = holds.Select(x => new { SomeNewId = x.Id, SomeNewName = x.Name });
var namedTuples = holds.Select(x => (TupleId: x.Id, TupleName: x.Name));
  1. 後でグループ化メソッドでタプルを再利用するか、メソッドを使用して他のロジックでインラインでタプルを構築します。
//Assuming holder class above making 'holds' object
public (int Id, string Name) ReturnNamedTuple(int id, string name) => (id, name);
public static List<(int Id, string Name)> ReturnNamedTuplesFromHolder(List<Hold> holds) => holds.Select(x => (x.Id, x.Name)).ToList();
public static void DoSomethingWithNamedTuplesInput(List<(int id, string name)> inputs) => inputs.ForEach(x => Console.WriteLine($"Doing work with {x.id} for {x.name}"));

var namedTuples2 = holds.Select(x => ReturnNamedTuple(x.Id, x.Name));
var namedTuples3 = ReturnNamedTuplesFromHolder(holds);
DoSomethingWithNamedTuplesInput(namedTuples.ToList());

あなたは私を古く感じさせる。:)ここで何か不足しているかもしれませんが、「保留」の初期化はまったく簡潔ではありません。これがまさに問題点でした。
Steven Jeuris

@Steven Jeurisいいえ、正解でした。ポイント1のコードを間違ってコピーして貼り付けました。「送信」を押す前に少し遅くする必要があります。
djangojazz

0

なぜタプルが好きですか?それは匿名型のようなもので、名前はありません。データの構造を理解できません。

クラシッククラスが好き

class FoodItem
{
     public int Position { get; set; }
     public string Name { get; set; }
}

List<FoodItem> list = new List<FoodItem>
{
     new FoodItem { Position = 1, Name = "apple" },
     new FoodItem { Position = 2, Name = "kiwi" }
};

3
私が述べたように:「それらはあなたがそれのために構造体やクラスを書くことを必要とせずに素早く関連する情報をグループ化することを可能にします。」このクラスが単一のメソッド内でヘルパーデータ構造としてのみ使用される場合は、不要なクラスを名前空間に入力しないように、タプルを使用することをお勧めします。
Steven Jeuris

1
あなたは(構造体ではなく)古いタプルクラスを考えています。C#7は名前を持つことができる構造体ベースのタプル(新しいValueTupleタイプに基づく)を追加したため、データの構造を理解できます
Steve Niles

0

私が思う1つのテクニックは少し簡単で、これは前にここで言及されていません:

var asdf = new [] { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
}.ToList();

私はそれが少しきれいだと思います:

var asdf = new List<Tuple<int, string>> { 
    (Age: 1, Name: "cow"), 
    (Age: 2, Name: "bird")
};

これは、型を明示的に言及するかどうかにかかわらず、使用するかどうかと同じように無駄に味わいますvar。私は個人的には明示的な型付けを好みます(コンストラクターなどで重複していない場合)。
Steven Jeuris 2018年

-4
    var colors = new[]
    {
        new { value = Color.White, name = "White" },
        new { value = Color.Silver, name = "Silver" },
        new { value = Color.Gray, name = "Gray" },
        new { value = Color.Black, name = "Black" },
        new { value = Color.Red, name = "Red" },
        new { value = Color.Maroon, name = "Maroon" },
        new { value = Color.Yellow, name = "Yellow" },
        new { value = Color.Olive, name = "Olive" },
        new { value = Color.Lime, name = "Lime" },
        new { value = Color.Green, name = "Green" },
        new { value = Color.Aqua, name = "Aqua" },
        new { value = Color.Teal, name = "Teal" },
        new { value = Color.Blue, name = "Blue" },
        new { value = Color.Navy, name = "Navy" },
        new { value = Color.Pink, name = "Pink" },
        new { value = Color.Fuchsia, name = "Fuchsia" },
        new { value = Color.Purple, name = "Purple" }
    };
    foreach (var color in colors)
    {
        stackLayout.Children.Add(
            new Label
            {
                Text = color.name,
                TextColor = color.value,
            });
        FontSize = Device.GetNamedSize(NamedSize.Large, typeof(Label))
    }

this is a Tuple<Color, string>

タプルを使用して値/名前の構文を取得するにはどうすればよいですか?
Andreas Reiff、2015年

コンパイラーは、匿名型が暗黙的にタプルに変換可能ではないことを示しています
cthepenier '23
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.