c# 删除重复算法 LINQ

c# Remove duplicates algorithm LINQ

我有一个 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.

第 3 个文件:

Student Names   Address
John            33 King st.

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

谢谢。

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

创建 StudentFileWriters 列表

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

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

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

假设你有一个class喜欢

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

我的想法是创建一个字典列表。 我们有学生 class (thx @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);
    }

简单版本,假设 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)));