2009-12-18 5 views
4

У меня есть функция, которая возвращает List<Dictionary<string, object>>, где объект является стандартным типом (строка, int и т. Д.).Сравнить 2 Список <Словарь <string, object >>

Мне нужно сделать второй List<Dictionary<string, object>> и убедиться, что все записи в списке В представлены в списке А (порядок в списке не имеет значения).

В настоящее время у меня есть код, который выглядит следующим образом:

foreach(Dictionary<string, object> rowResult in A) { 
    foreach(Dictionary<string, object> rowCompare in B) { 
    foreach(string columnName in rowResult.Keys) { 
     // ... logic to compare columns 
    } 
    } 

    // ...logic to compare rows so we dont find the same row twice. 
} 

Есть ли более простой способ сделать это?

Мы не заботимся о том, чтобы все строки в rowResult были найдены, но все строки в rowCompare должны быть. Это ОК, чтобы удалить строки из сравнения или набора результатов, чтобы упростить итерацию.

Мой код работает, он просто выглядит сложным и хрупким.

+0

Не домашнее задание. Его для сравнения рядов ad-hoc. Я не хотел добавлять всю логику сравнения, поскольку она не имеет отношения к проблеме. – GrayWizardx

+0

Без проблем, Слэкс просто следил за тем, чтобы все базы были покрыты. –

+0

Все хорошо, просто хотел уточнить. – GrayWizardx

ответ

6
class DictionaryComparer<TKey, TValue> : IEqualityComparer<Dictionary<TKey, TValue>> {  
    public bool Equals(Dictionary<TKey, TValue> x, Dictionary<TKey, TValue> y) { 
     if (x == null) { 
      throw new ArgumentNullException("x"); 
     } 
     if (y == null) { 
      throw new ArgumentNullException("y"); 
     } 
     if (x.Count != y.Count) { 
      return false; 
     }   
     foreach (var kvp in x) { 
      TValue value; 
      if(!y.TryGetValue(kvp.Key, out value)) { 
       return false; 
      } 
      if(!kvp.Value.Equals(value)) { 
       return false; 
      } 
     } 
     return true; 
    } 

    public int GetHashCode(Dictionary<TKey, TValue> obj) { 
     if (obj == null) { 
      throw new ArgumentNullException("obj"); 
     } 
     int hash = 0; 
     foreach (var kvp in obj) { 
      hash = hash^kvp.Key.GetHashCode()^kvp.Value.GetHashCode(); 
     } 
     return hash; 
    } 
} 

Тогда:

public bool Contains(
    List<Dictionary<string, object>> first, 
    List<Dictionary<string, object>> second) { 
    if(first == null) { 
     throw new ArgumentNullException("first"); 
    } 
    if(second == null) { 
     throw new ArgumentNullException("second"); 
    } 
    IEqualityComparer<Dictionary<string, object>> comparer = new DictionaryComparer<string, object>(); 
    return second.All(y => first.Contains(y, comparer)); 
} 
+0

Интересно. Я забыл о реализации собственного хэша. – GrayWizardx

+0

fyi MS.NET 3.5 doc для «IEqualityComparer <(Of <(T>)>) Интерфейс«: «Мы рекомендуем вам вывести из EqualityComparer <(Of <(T>)>) класс вместо реализации интерфейса IEqualityComparer <(Of <(T>)>, потому что класс EqualityComparer <(Of <(T>)>) тесты для равенства, используя метод IEquatable <(Of <(T>)>) .. ::. Equals вместо метода Object .. ::. Equals. Это согласуется с методами Contains, IndexOf, LastIndexOf и Remove словаря <(Of < (TKey, TValue>)>) класс и другие общие коллекции ": этот комментарий отсутствует в текущих документах FrameWork 4. Надеюсь, это актуально. – BillW

+0

Там это ошибка в общественном BOOL Equals (Dictionary х, Dictionary у) Должно быть .... если (! Y.TryGetValue (kvp.Key, из значения)) –

1
foreach(Dictionary<string, object> rowCompare in B) 
    if(!A.Contains(rowCompare)) return false; 
return true; 

Предполагая, что ваши компараторы настроены соответствующим образом, это сработает. И прост и понятен. Вы даже можете добавить фигурные скобки, если вам будет проще.

+0

Я не уверен, что на самом деле сравнивает содержимое словарей. У меня был аналогичный код, и он всегда возвращал false. Каждый ключ в словаре необходимо сравнить с его соответствующим ключом в другом словаре. Я попробую. – GrayWizardx

+0

Вы почти наверняка должны настроить пользовательский 'IEqualityComparer' для этого, чтобы это работало. – jason

+0

Есть причина, по которой я включил фразу * Предполагая, что ваши компараторы настроены соответствующим образом в моем ответе. –

2

Ну ... что ты делаешь это для? Причина, по которой я спрашиваю, заключается в том, что этот гиперподобный метод Unit Testing называется CollectionAssert.AreEquivalent.

В разделе примечаний:

Две коллекции эквивалентны, если они имеют одни и те же элементы в том же количестве, но и в любом порядке. Элементы равны, если их значения равны, а не если они относятся к одному и тому же объекту.

Похоже, он делает именно то, что вы хотите, и выбрасывает AssertFailedException, если они не эквивалентны, что также может быть тем, что вы хотите. Есть ли способ использовать его? Один вызов метода, безусловно, намного больше, чем тройной вложенный цикл - даже если это то, что метод делает под обложками.

+0

У меня есть система, которая возвращает результаты в виде списка <Словарь <строка, объект >> данные фиксированный набор, и мне нужно убедиться, что подмножество этих данных существует. A =/= B, но B == A (sub) [некоторое подмножество A] это также верно и для словаря (у B может быть меньше записей, но все записи должны быть в A) – GrayWizardx

+0

Ах, я вижу. Не беспокойся в таком случае. :) – Randolpho

+0

Я проверил это. Очень интересно, не что-то для этой работы, но хорошая информация. Благодарю. – GrayWizardx

Смежные вопросы