【问题标题】:c# Remove duplicates algorithm LINQc# 删除重复算法LINQ
【发布时间】:2015-09-18 15:03:54
【问题描述】:

我有一个 csv 文件,如下所示:学生姓名、地址。

但是,学生姓名列可能有重复,所以如果是这种情况,我需要创建一个新文件,其中只有那些重复的学生姓名和地址 - 继续进行,直到每个文件在特定文件中没有重复的学生姓名。

即。

Student Names   Address
John            5 West st.
David           42 Alan st.
John            22 Dees st.
Smith           2 King st.
David           77 Jack st.
John            33 King st.

应该像这样分成3个文件: 第一个文件:

Student Names   Address
John            5 West st.
David           42 Alan st.
Smith           2 King st.

第二个文件:

Student Names   Address
John            22 Dees st.
David           77 Jack st.

第三个文件:

Student Names   Address
John            33 King st.

我的逻辑是将文件放入 DataTable 并创建学生姓名字典 -> 地址 - 但是,字典不起作用,因为它们的键不是唯一的。所以我的下一个逻辑是创建一个学生姓名列表并从中找出重复项并创建一个数据表并从那里创建一个文件。我觉得这更复杂 - 我很确定在 LiNQ 中一定有更简单的方法 - 你们能帮我解决一下吗?

谢谢。

【问题讨论】:

  • 您正在寻找Lookup<Tkey,TValue>

标签: c# linq csv datatable


【解决方案1】:

Dictionary 方法实际上相当不错。我会坚持下去。将字典的键、名称和值作为地址。这样,您将通过查找具有最多地址的名称来知道需要创建多少个文件。地址数将是您需要创建的文件数。

然后浏览名称列表并将它们和地址按顺序添加到单独的文件中。然后,一旦用尽所有名称,您就完成了。

在上面的示例中,您将拥有这样的字典

John -> 5 West st., 22 Dees st., 33 King st.
David -> 42 Alan st., 77 Jack st.    
Smith -> 2 King st.

正如@ric 所说,这将是Dictionary<string, List<string>>

【讨论】:

  • 你的意思是Dictionary<string, List<string>>?我想您必须从列表中获取 max 计数才能计算出您需要的文件数
【解决方案2】:

假设你有一个类

public class Student
{
    public string Name { get; set; }
    public string Address { get; set; }
}

在 linq 中,您可以按姓名对学生进行分组

 var students = LoadStudentsFromFile();
 var studentsByName = students.GroupBy(st => st.Name).ToDictionary(g => g.Key, g => g.ToList());

此时您将拥有一个Dictionary,其中学生姓名作为键,学生列表作为值

John ->  [{Name: John, Address: 5 West st.}, {Name: John, Address: 22 Dees st.}, {Name: John, Address: 33 King st.}]
David -> [{Name: David, Address: 42 Alan st.}, {Name: David, Address: 277 Jack st.}]
...

然后您可以遍历键并从每个键的末尾取一个,直到清空列表和字典。从末尾开始,以避免重新调整列表大小。

 while(studentsByName.Any())
 {
     var uniqueStudents = new List<Student>();
     foreach(var name in studentsByName.Keys)
     {
         uniqueStudents.Add(studentsByName[name].Last());
         studentsByName[name].RemoveAt(studentsByName[name].Count -1);
         if(studentsByName[name].Count == 0)
         {
             studentsByName.Remove(name);
         }
     }

     SaveListOfUniqueStudents(uniqueStudents);
 }

【讨论】:

  • 这很聪明,但对这个方法返回的内容有点困惑:LoadStudentsFromFile() - 它是一个数据表吗?
  • @civic.sir 应该是IEnumerable&lt;Student&gt;(例如:Student[]List&lt;Student&gt;IQueryable&lt;Student&gt; 等)
  • 这是一个原始示例:IEnumerable&lt;Student&gt; LoadStudentsFromFile(string path) { return File.ReadLines(path).Select(x=&gt;{ var fields=x.Split(','); return new Student {Name=fields[0],Id=field[1]}); }
  • 非常感谢 Limo 和 Robert。我对这个算法进行了一些更改,但它现在可以工作了。再次感谢
【解决方案3】:

简单版本,假设 CSV 很简单,以逗号分隔,并且不允许将字符串括在双引号中,但如果您需要,可以对其进行扩展:

IEnumerable<Student> LoadStudentsFromFile(string path)
{
  return File.ReadLines(path).Select(x=>{
    var fields=x.Split(','); 
    return new Student {Name=fields[0],Id=field[1]});
}
void SaveStudentsToFile(path,IEnumerable<Student> students)
{
  File.WriteAllLines(path,students);
}
var students=LoadStudentsFromFile("inputfile.csv");
var studentsByName = students.GroupBy(st => st.Name)
  .ToDictionary(g => g.Key, g => g.ToList());

var max=studentsByName.Max(x=>x.Value.Count());
for(var x=0;x<max;x++)
  SaveStudentsToFile("outfile"+x+".csv",
    studentsByName.Where(s=>s.Value.Count()>=x+1)
      .Select(s=>string.Format("{0},{1}",s.Key,s.Value.Skip(x).First)));

【讨论】:

    【解决方案4】:

    我会选择类似的东西: 创建一个类 (StudentFileWriter),其中包含 CSV 文件的 Writer 和该文件中的名称列表。每当您写入文件时,将名称添加到列表中。

    创建 StudentFileWriters 列表

    然后一次读取一行文件,检查第一个 StudentFileWriter 是否为 ListOfNames.Contains(string newNameToInsert) 如果为真,则转到下一个,如果没有新的,则创建一个并将其写入新文件。 如果为 false,只需写入它的文件。

    您也可以使用 Groupings/Rankings 等在很复杂的 Linq 中编写它,但这样应该很容易调试并查看发生了什么。

    【讨论】:

      【解决方案5】:

      我的想法是创建一个字典列表。 我们有学生课(谢谢@LimoWanKenobi):

      public class Student
      {
          public string Name { get; set; }
          public string Address { get; set; }
      }
      

      这是我的方法:

          IEnumerable<IEnumerable<Student>> Process(IEnumerable<Student> students)
          {
              var files = new List<Dictionary<string, Student>>();
      
              foreach (var student in students)
              {
                  var isAdded = false;
                  foreach (var file in files)
                  {
                      if (!file.ContainsKey(student.Name))
                      {
                          file.Add(student.Name, student);
                          isAdded = true;
                          break;
                      }
                  }
      
                  if (!isAdded)
                  {
                      files.Add(new Dictionary<string, Student>
                      {
                          { student.Name, student }
                      });
                  }
              }
      
              return files.Select(kvp => kvp.Values);
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-04-17
        • 1970-01-01
        • 2010-11-04
        • 1970-01-01
        • 1970-01-01
        • 2011-03-09
        • 1970-01-01
        相关资源
        最近更新 更多