2013-03-05 4 views
0

У меня есть человек класс, как так:Получение повторных данных из CSV файла

class Person 
{ 
    string Id { get; set; } 
    string FirstName { get; set; } 
    string LastName { get; set; } 
} 

Существует CSV-файл, который имеет человеко данные, такие как

"123","ABC","DEF" 
"456","GHI","JKL" 
"123","MNO","PQR" 
... 

Человек уникален на основе Id ,

CSV-читается так:

using (StreamReader sr = new StreamReader(inputFile)) 
{ 
    string[] arrCsvData; 
    string strLine; 

    while ((strLine = sr.ReadLine()) != null) 
    { 
     arrCsvData = strLine.Split(','); 
     this.LoadPersonData(arrCsvData); 
    } 
} 

В LoadPersonData новый объект Person создается и присваиваются значения из CSV:

Person objPerson = new Person(); 
for (int i = 1; i <= arrCsvData.Length - 1; i++) 
{ 
    // Assign person property values from arrCsvData 
} 

У меня есть объект словаря, в котором ключ является идентификатор и значение - это объект Person.

if(!this.PersonDataCollection.ContainsKey(personKey)) 
{ 
    this.PersonDataCollection.Add(objPerson); 
} 

Это дает мне все уникальные объекты Person из файла CSV.

Я хочу создать список тех объектов Person, которые повторяются на основе Id в CSV. Так что список DuplicatePersons будет:

"123","ABC","DEF" 
"123","MNO","PQR" 

в нем.

Путь с пустыми костями заключается в том, чтобы сначала прочитать все объекты человека в списке, а затем выполнить запрос LINQ, чтобы получить все дубликаты в отдельном списке. Таким образом, я должен создать дополнительную коллекцию, чтобы получить дубликаты.

Должен быть лучший способ создать отдельный список.

Любые указатели?

ответ

0

Прежде всего, я хотел бы использовать LINQToCSV. Разбор CSV-файлов более сложный, чем просто разделение на ,.Вам не нужно кодировать ничего, просто создать свой класс, и место атрибуты на нем:

class Person 
{ 
    [CsvColumn(Name = "ID", ...)] 
    string Id { get; set; } 
    [CsvColumn(Name = "First Name", ...)] 
    string FirstName { get; set; } 
    [CsvColumn(Name = "Last Name", ...)] 
    string LastName { get; set; } 
} 

Затем, когда вы читаете файл с помощью LINQToCSV, вы получите IEnumerable<Person> ... и после этого вы можете сделать:

IEnumerable<Person> people = ... //read here using LINQToCSV 
var grouped = people.GroupBy(p => p.Id); 

Если вы будете знать уникальный столбец во время выполнения, вы можете сделать что-то вроде этого:

string columnName = "Id"; 
persons.GroupBy(x => x.GetType().GetProperty(columnName).GetValue(x, null)); 

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

Dictionary<string, Func<Person, object>> selectors = new Dictionary <string, Func<Person, object>> 
      { 
       {"Id", x => x.Id}, 
       {"FirstName", x => x.FirstName}, 
       {"LastName", x => x.LastName}, 
      }; 

string columnName = "Id"; 
var grouped = people.GroupBy(selectors[columnName]); 

Теперь, используя свой подход ... что случилось с созданием другой словарь?

Вы могли бы что-то вроде:

//Here you will store the duplicated person 
//Key: The person Id 
//Value: The list of person sharing that same Id 
Dictionary<string, IList<Person>> duplicatedPeople; 


if(!this.PersonDataCollection.ContainsKey(personKey)) 
{ 
    this.PersonDataCollection.Add(objPerson); 
} 
else 
{ 
    //Here we store all the people with this already existing ID 
    IList<Person> duplicatedPeople; 

    //If we already have this ID in the dictionary of repeated people 
    if (this.duplicatedPeople.TryGetValue(personKey, out duplicatedPeople)) { 
     //Just add this new person 
     duplicatedPeople.Add(objPerson); 
    } 
    //If this is the 1st time we found a duplicated person with this ID 
    else { 
     //We add two persons to the list: this one, and the one from PersonDataCollection. 
     duplicatedPeople = new List<Person> { this.PersonDataCollection[personKey], objPerson }; 
     //Add it to the dictionary 
     this.duplicatedPeople.Add(personKey, duplicatedPeople); 
    } 
} 
+0

является там способ динамически делать это var grouped = people.GroupBy (p => p.Id); Я имею в виду, можно ли уникальность выполнить во время выполнения? Пользователь может установить уникальный столбец в файле конфигурации. – Codehelp

+0

hmm ... обязательно. Позвольте мне отредактировать мой ответ. –

+0

@Codehelp отредактирован. –

0

Почему бы вам не проверить, существуют ли значения в этой точке.

Person objPerson = new Person(); 
for (int i = 1; i <= arrCsvData.Length - 1; i++) 
{ 
     // Assign person property values from arrCsvData 
} 

Проверьте здесь ваше состояние и сделайте то, что захотите, с повторяющимися значениями в этой точке.

0

Что бы вы ни делали, всегда будет отдельный список. Это зависит от вас от того, как вы хотите, чтобы они появились.

Вариант 1 - Временные списки


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

Вариант 2 - статический список


Почему не просто сохранить свой собственный список на данный момент ?:

if(!this.PersonDataCollection.ContainsKey(personKey)) 
{ 
    this.PersonDataCollection.Add(objPerson); 
} 
else 
{ 
    // Create a new dictionary for the duplicates 
    this.DuplicatePersonDataCollection.Add(objPerson); 
} 
0

Создать единый список для всех лиц и достаточно запросить его с помощью LINQ, чтобы получить результаты:

т.е.

var persons = new List<Person>(); 
persons.Add(new Person { Id = "123", FirstName = "AAA", LastName = "XXX" }); 
persons.Add(new Person { Id = "123", FirstName = "BBB", LastName = "WWW" }); 
persons.Add(new Person { Id = "456", FirstName = "CCC", LastName = "XXX" }); 
persons.Add(new Person { Id = "456", FirstName = "DDD", LastName = "YYY" }); 
persons.Add(new Person { Id = "789", FirstName = "EEE", LastName = "ZZZ" }); 

var duplicateKeys = persons.GroupBy(p => p.Id).Select(g => new { g.Key, Count = g.Count() }).Where(x => x.Count > 1).ToList().Select(d => d.Key); 
var duplicatePersons = persons.Where(p => duplicateKeys.Contains(p.Id)).ToList(); 
var unique = persons.GroupBy(p => p.Id).ToList();