2010-01-20 45 views
54

Tôi muốn tạo một kho dữ liệu để cho phép tôi lưu trữ một số dữ liệu.C# từ điển một khóa nhiều giá trị

Ý tưởng đầu tiên là tạo một từ điển mà bạn có 1 khóa với nhiều giá trị, do đó, giống như một mối quan hệ một đến nhiều.

Tôi nghĩ từ điển chỉ có 1 giá trị khóa.

Tôi có thể lưu trữ thông tin này bằng cách nào khác?

Trả lời

45

Bạn có thể sử dụng danh sách cho loại chung thứ hai. Ví dụ: từ điển chuỗi được khóa bằng một chuỗi:

Dictionary<string, List<string>> myDict; 
5

Bạn có thể sử dụng Dictionary<TKey, List<TValue>>.

Điều đó sẽ cho phép mỗi khóa tham chiếu danh sách giá trị.

1

Bạn có thể có từ điển với bộ sưu tập (hoặc bất kỳ loại/lớp nào khác) làm giá trị. Bằng cách đó bạn có một khóa duy nhất và bạn lưu trữ các giá trị trong bộ sưu tập của mình.

1

Từ điển .NET chỉ có mối quan hệ 1 đến 1 cho khóa và giá trị. Nhưng điều đó không có nghĩa là một giá trị không thể là một mảng/danh sách/từ điển khác.

Tôi không thể nghĩ ra lý do để có mối quan hệ 1 đến nhiều trong từ điển, nhưng rõ ràng là có một.

Nếu bạn có các loại dữ liệu khác nhau mà bạn muốn lưu trữ vào khóa, thì âm thanh đó giống như thời gian lý tưởng để tạo lớp của riêng bạn. Sau đó, bạn có 1 đến 1, nhưng bạn có lớp giá trị lưu trữ nhiều hơn 1 mẩu dữ liệu.

6

Loại giá trị của từ điển của bạn có thể là Danh sách hoặc lớp khác chứa nhiều đối tượng. Một cái gì đó như

Dictionary<int, List<string>> 

cho từ điển được khóa bởi ints và giữ một Danh sách các chuỗi.

Cân nhắc chính trong việc chọn loại giá trị là những gì bạn sẽ sử dụng Từ điển, nếu bạn phải thực hiện tìm kiếm hoặc các hoạt động khác trên giá trị, thì có thể suy nghĩ về việc sử dụng cấu trúc dữ liệu giúp bạn những gì bạn muốn - như một HashSet.

4

Sử dụng một cuốn từ điển các danh sách (hoặc một loại bộ sưu tập), ví dụ:

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

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

Tính đến .net3.5 + thay vì sử dụng một Dictionary<IKey, List<IValue>> bạn có thể sử dụng một Lookup từ namespace LINQ:

// 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]; 

Từ msdn:

một Lookup giống như một từ điển. Sự khác biệt là một khóa bản đồ từ điển cho các giá trị đơn lẻ, trong khi đó, các khóa bản đồ Lookup để thu thập các giá trị .

Bạn có thể tạo phiên bản của Tìm kiếm bằng cách gọi ToLookup trên một đối tượng triển khai IEnumerable.

Bạn cũng có thể muốn đọc this answer đến related question. Để biết thêm thông tin, hãy tham khảo msdn.

Full dụ:

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

Ghi chú về tính bất biến

Theo mặc định, tra cứu là loại không thể thay đổi và truy cập vào internal s sẽ bao gồm phản ánh. Nếu bạn cần sự biến đổi và không muốn viết trình bao bọc riêng, bạn có thể sử dụng MultiValueDictionary (trước đây gọi là MultiDictionary) từ corefxlab (trước đây là một phần của Microsoft.Experimental.Collections không được cập nhật nữa).

+0

tra cứu là không thay đổi, xin vui lòng chỉ này ra trong câu trả lời của bạn. – Shimmy

+0

Khám phá [câu trả lời của tôi] (https://stackoverflow.com/a/45824919/75500). – Shimmy

+2

@Shimmy được cập nhật theo yêu cầu – mbx

7

Sử dụng này:

Dictionary<TKey, Tuple<TValue1, TValue2, TValue3, ...>> 
16

Microsoft vừa mới thêm một phiên bản chính thức của prelease chính xác những gì bạn đang tìm kiếm (gọi là MultiDictionary) có sẵn thông qua NuGet đây: https://www.nuget.org/packages/Microsoft.Experimental.Collections/

Thông tin về việc sử dụng và biết thêm chi tiết có thể được tìm thấy thông qua bài đăng blog chính thức của MSDN tại đây: http://blogs.msdn.com/b/dotnet/archive/2014/06/20/would-you-like-a-multidictionary.aspx

Tôi là nhà phát triển cho gói này, vì vậy hãy cho tôi biết tại đây hoặc trên MSDN nếu bạn có bất kỳ câu hỏi nào về hiệu suất hoặc anythi ng.

Hy vọng điều đó sẽ hữu ích.

Cập nhật

Các MultiValueDictionary tại là trên corefxlab repo, và bạn có thể nhận được gói NuGet từ this thức ăn MyGet.

+0

Có vẻ như giờ đây nó được gọi là MultiValueDictionary. Tôi muốn sử dụng điều này, nhưng không chắc chắn về tương lai của nó. Blog chưa được cập nhật trong 3 năm. Bất kỳ ý tưởng nào về việc liệu điều này có an toàn để sử dụng không? –

+0

Tôi chưa thử nghiệm với 'MultiValueDictionary', nhưng nó thực hiện' IReadOnlyDictionary' là không thay đổi. Dù sao tôi đã cập nhật câu trả lời của bạn, trông giống như công cụ đó chuyển đến repo corefxlab. – Shimmy

0

Đây là cách tiếp cận của tôi để đạt được hành vi này.

Để có giải pháp toàn diện hơn liên quan đến ILookup<TKey, TElement>, hãy xem 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); 
    } 
    } 
} 

Cách sử dụng:

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

Lưu ý: phím phải bất biến (hoặc loại bỏ và tái thêm khi chìa khóa thay đổi).

0

Bạn cũng có thể sử dụng;

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

Bạn có thể tạo rất đơn giản đa từ điển, mà tự động đến quá trình chèn giá trị như thế này:

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

Điều này tạo ra một phiên bản quá tải của phương pháp Add. Bản gốc cho phép bạn chèn một danh sách các mục cho một khóa, nếu chưa có mục nhập cho mục này. Phiên bản này cho phép bạn chèn một mục duy nhất trong mọi trường hợp.

0

Hãy xem MultiValueDictionary từ Microsoft.

Ví dụ Code:

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 
Các vấn đề liên quan