2016-05-17 4 views
4

Я читаю данные из файлов (например, CSV и Excel) и должен гарантировать, что каждая строка в файле уникальна.Проверка уникальности для массива объектов

Каждая строка будет представлена ​​как object[]. Это невозможно изменить из-за текущей архитектуры. Каждый объект в этом массиве может быть разного типа (decimal, string, int и т. Д.).

Файл может выглядеть следующим образом:

foo 1  5 // Not unique 
bar 1  5 
bar 2  5 
foo 1  5 // Not unique 

Файл может иметь 200.000+ строк и столбцов 4-100.

код я прямо сейчас выглядит следующим образом:

IList<object[]> rows = new List<object[]>(); 

using (var reader = _deliveryObjectReaderFactory.CreateReader(deliveryObject)) 
{ 
    // Read the row. 
    while (reader.Read()) 
    { 
     // Get the values from the file. 
     var values = reader.GetValues(); 

     // Check uniqueness for row 
     foreach (var row in rows) 
     { 
      bool rowsAreDifferent = false; 

      // Check uniqueness for column. 
      for (int i = 0; i < row.Length; i++) 
      { 
       var earlierValue = row[i]; 
       var newValue = values[i]; 
       if (earlierValue.ToString() != newValue.ToString()) 
       { 
        rowsAreDifferent = true; 
        break; 
       } 
      } 
      if(!rowsAreDifferent) 
       throw new Exception("Rows are not unique"); 
     } 
     rows.Add(values); 
    } 
} 

Итак, мой вопрос, можно ли это сделать более эффективно? Например, использование хэшей и проверка уникальности хэша вместо этого?

+0

Вы понимаете, что возможно, что два объекта имеют одинаковый хеш и по-прежнему неравны, не так ли? Другими словами, если ваш хеш сделан правильно, файл может иметь повторяющиеся хэши, но все равно иметь уникальные строки. – phoog

+1

Как насчет использования HashSet с пользовательским сопоставлением равенства? – Jehof

+0

@phoog, да, я хорошо знаю об этом. Решение сначала проверит хэш, и если хеши будут равны, ему придется также проверять и другие значения. Но, возможно, лучше проверить хэш сначала, а не всегда проверять все значения. – smoksnes

ответ

4

Вы можете использовать HashSet<object[]> с обычаем IEqualityComparer<object[]> так:

HashSet<object[]> rows = new HashSet<object[]>(new MyComparer()); 

while (reader.Read()) 
{ 
    // Get the values from the file. 
    var values = reader.GetValues();  
    if (!rows.Add(values)) 
     throw new Exception("Rows are not unique"); 
} 

И MyComparer может быть реализован так:

public class MyComparer : IEqualityComparer<object[]> 
{ 
    public bool Equals(object[] x, object[] y) 
    { 
     if (ReferenceEquals(x, y)) return true; 
     if (ReferenceEquals(x, null) || ReferenceEquals(y, null) || x.Length != y.Length) return false; 
     return x.Zip(y, (a, b) => a == b).All(c => c); 
    } 
    public int GetHashCode(object[] obj) 
    { 
     unchecked 
     { 
      // this returns 0 if obj is null 
      // otherwise it combines the hashes of all elements 
      // like hash = (hash * 397)^nextHash 
      // if an array element is null its hash is assumed as 0 
      // (this is the ReSharper suggestion for GetHashCode implementations) 
      return obj?.Aggregate(0, (hash, o) => (hash * 397)^(o?.GetHashCode() ?? 0)) ?? 0; 
     } 
    } 
} 

Я не совсем уверен, что если часть работ a==b для всех типов.

+0

oh, только что увидел, что @Jehof уже предлагал это во время написания, поэтому вы, вероятно, уже знали, как это сделать ... –

+0

Да, я пытаюсь Теперь. Но без фантастических возможностей C# 6. ;) – smoksnes

+0

Это последнее заявление возврата выглядит для меня пугающе. Мне, вероятно, понадобится хорошее количество кофе и 15 минут времени, чтобы понять, почему он делает то, что он делает. Не возражаете ли вы добавить строку или две, комментируя оператор '?' И почему вы умножаетесь на 391? – Marco