web-dev-qa-db-ja.com

C#辞書1つのキー多くの値

データストアを作成して、データを保存できるようにします。

最初のアイデアは、多くの値を持つ1つのキーを持つ辞書を作成することで、1対多の関係に少し似ています。

辞書には1つのキー値しかないと思います。

この情報を他にどのように保存できますか?

62
Marc G

.net3.5 +以降、Dictionary<IKey, List<IValue>>を使用する代わりに、Linq名前空間のLookupを使用できます。

// lookup Order by payment status (1:m) 
// would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed
ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed);
IEnumerable<Order> payedOrders = byPayment[false];

msdn から:

ルックアップは辞書に似ています。違いは、ディクショナリはキーを単一の値にマップするのに対して、ルックアップはキーを値のコレクションにマップすることです。

IEnumerableを実装するオブジェクトでToLookupを呼び出すことにより、Lookupのインスタンスを作成できます。

この回答 から 関連する質問 を読むこともできます。詳細については、 msdn を参照してください。

完全な例:

using System;
using System.Collections.Generic;
using System.Linq;

namespace LinqLookupSpike
{
    class Program
    {
        static void Main(String[] args)
        {
            // init 
            var orderList = new List<Order>();
            orderList.Add(new Order(1, 1, 2010, true));//(orderId, customerId, year, isPayed)
            orderList.Add(new Order(2, 2, 2010, true));
            orderList.Add(new Order(3, 1, 2010, true));
            orderList.Add(new Order(4, 2, 2011, true));
            orderList.Add(new Order(5, 2, 2011, false));
            orderList.Add(new Order(6, 1, 2011, true));
            orderList.Add(new Order(7, 3, 2012, false));

            // lookup Order by its id (1:1, so usual dictionary is ok)
            Dictionary<Int32, Order> orders = orderList.ToDictionary(o => o.OrderId, o => o);

            // lookup Order by customer (1:n) 
            // would need something like Dictionary<Int32, IEnumerable<Order>> orderIdByCustomer
            ILookup<Int32, Order> byCustomerId = orderList.ToLookup(o => o.CustomerId);
            foreach (var customerOrders in byCustomerId)
            {
                Console.WriteLine("Customer {0} ordered:", customerOrders.Key);
                foreach (var order in customerOrders)
                {
                    Console.WriteLine("    Order {0} is payed: {1}", order.OrderId, order.IsPayed);
                }
            }

            // the same using old fashioned Dictionary
            Dictionary<Int32, List<Order>> orderIdByCustomer;
            orderIdByCustomer = byCustomerId.ToDictionary(g => g.Key, g => g.ToList());
            foreach (var customerOrders in orderIdByCustomer)
            {
                Console.WriteLine("Customer {0} ordered:", customerOrders.Key);
                foreach (var order in customerOrders.Value)
                {
                    Console.WriteLine("    Order {0} is payed: {1}", order.OrderId, order.IsPayed);
                }
            }

            // lookup Order by payment status (1:m) 
            // would need something like Dictionary<Boolean, IEnumerable<Order>> orderIdByIsPayed
            ILookup<Boolean, Order> byPayment = orderList.ToLookup(o => o.IsPayed);
            IEnumerable<Order> payedOrders = byPayment[false];
            foreach (var payedOrder in payedOrders)
            {
                Console.WriteLine("Order {0} from Customer {1} is not payed.", payedOrder.OrderId, payedOrder.CustomerId);
            }
        }

        class Order
        {
            // key properties
            public Int32 OrderId { get; private set; }
            public Int32 CustomerId { get; private set; }
            public Int32 Year { get; private set; }
            public Boolean IsPayed { get; private set; }

            // additional properties
            // private List<OrderItem> _items;

            public Order(Int32 orderId, Int32 customerId, Int32 year, Boolean isPayed)
            {
                OrderId = orderId;
                CustomerId = customerId;
                Year = year;
                IsPayed = isPayed;
            }
        }
    }
}

不変性に関する注意

デフォルトでは、ルックアップは一種の不変であり、internalsへのアクセスにはリフレクションが含まれます。可変性が必要で、独自のラッパーを作成したくない場合は、 MultiValueDictionary (以前は MultiDictionary )を使用できます。 corefxlab (以前の一部 Microsoft.Experimental.Collections は更新されていません)。

68
mbx

2番目の汎用タイプのリストを使用できます。たとえば、文字列をキーとする文字列の辞書:

Dictionary<string, List<string>> myDict;
51
Oded

Microsoftは、NuGetから入手できる、まさに探しているものの公式リリース版(MultiDictionaryと呼ばれる)を追加しました: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

使用方法と詳細については、MSDNの公式ブログ投稿をご覧ください: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a -multidictionary.aspx

私はこのパッケージの開発者です。パフォーマンスまたは何かについて質問がある場合は、こちらまたはMSDNでお知らせください。

お役に立てば幸いです。

更新

MultiValueDictionarycorefxlabリポジトリ にあり、NuGetパッケージは this MyGetフィードから取得できます。

16
Ian Hays

辞書の値タイプは、リスト、または複数のオブジェクトを保持する他のクラスです。何かのようなもの

Dictionary<int, List<string>> 

intをキーとし、文字列のリストを保持する辞書の場合。

値タイプを選択する際の主な考慮事項は、値の検索または他の操作を実行する必要がある場合、辞書を使用することです。 -HashSetのような。

7
Tim Ridgely

これを使って:

Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>>
7
Gisway

Dictionary<TKey, List<TValue>>を使用できます。

これにより、各キーがlistの値を参照できます。

5
Blorgbeard

リストの辞書(または別のタイプのコレクション)を使用します。例:

var myDictionary = new Dictionary<string, IList<int>>();

myDictionary["My key"] = new List<int> {1, 2, 3, 4, 5};
4
GraemeF

コレクション(またはその他のタイプ/クラス)を値として持つ辞書を作成できます。そのようにして、単一のキーを持ち、値をコレクションに保存します。

1
Maurits Rijk

.NETディクショナリには、キーと値の1対1の関係しかありません。しかし、それは値が別の配列/リスト/辞書になれないという意味ではありません。

辞書に1対多の関係がある理由は考えられませんが、明らかに関係があります。

キーに保存するデータの種類が異なる場合は、独自のクラスを作成するのが理想的です。次に、1対1がありますが、1つ以上のデータを格納する値クラスがあります。

1
Alastair Pitts

Microsoftの MultiValueDictionary をご覧ください。

サンプルコード:

MultiValueDictionary<string, string> Parameters = new MultiValueDictionary<string, string>();

Parameters.Add("Malik", "ALi");
Parameters.Add("Malik", "Hamza");
Parameters.Add("Malik", "Danish");

//Parameters["Malik"] now contains the values ALi, Hamza, and Danish
1
Ali Asad

次のような値を挿入するプロセスを自動化する、非常に単純なマルチ辞書を作成できます。

public class MultiDictionary<TKey, TValue> : Dictionary<TKey, List<TValue>>
{
    public void Add(TKey key, TValue value)
    {
        if (TryGetValue(key, out List<TValue> valueList)) {
            valueList.Add(value);
        } else {
            Add(key, new List<TValue> { value });
        }
    }
}

これにより、Addメソッドのオーバーロードバージョンが作成されます。元のエントリでは、このエントリのエントリがまだ存在しない場合、キーのアイテムのリストを挿入できます。このバージョンでは、どのような場合でも単一のアイテムを挿入できます。

この動作を実現するための私のアプローチは次のとおりです。

ILookup<TKey, TElement>を含むより包括的なソリューションについては、 my other answer をご覧ください。

public abstract class Lookup<TKey, TElement> : KeyedCollection<TKey, ICollection<TElement>>
{
  protected override TKey GetKeyForItem(ICollection<TElement> item) =>
    item
    .Select(b => GetKeyForItem(b))
    .Distinct()
    .SingleOrDefault();

  protected abstract TKey GetKeyForItem(TElement item);

  public void Add(TElement item)
  {
    var key = GetKeyForItem(item);
    if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
      collection.Add(item);
    else
      Add(new List<TElement> { item });
  }

  public void Remove(TElement item)
  {
    var key = GetKeyForItem(item);
    if (Dictionary != null && Dictionary.TryGetValue(key, out var collection))
    {
      collection.Remove(item);
      if (collection.Count == 0)
        Remove(key);
    }
  }
}

使用法:

public class Item
{
  public string Key { get; }
  public string Value { get; set; }
  public Item(string key, string value = null) { Key = key; Value = value; }
}

public class Lookup : Lookup<string, Item>
{
  protected override string GetKeyForItem(Item item) => item.Key;
}

static void Main(string[] args)
{
  var toRem = new Item("1", "different");
  var single = new Item("2", "single");
  var lookup = new Lookup()
  {
    new Item("1", "hello"),
    new Item("1", "hello2"),
    new Item(""),
    new Item("", "helloo"),
    toRem,
    single
  };

  lookup.Remove(toRem);
  lookup.Remove(single);
}

注:キーは不変である必要があります(または、キーの変更時に削除して再追加します)。

0
Shimmy

使用することもできます。

 List<KeyValuePair<string, string>> Mappings;
0
Teoman shipahi