【问题标题】:How to get values AND indices of duplicate items in a list?如何获取列表中重复项的值和索引?
【发布时间】:2013-03-19 22:28:41
【问题描述】:

我有一个文件名列表 (targetFileList),其中一些是重复的(例如,我有两个名为 m4.txt 的文件)。以下语句查找重复的文件名并将它们添加到另一个列表 (currentTargetFiles):

currentTargetFiles = targetFileList.FindAll(item => item == baselineFilename);

事实上,这一行返回一个字符串列表(文件名),这很好,但我还需要它们的索引值。有没有办法修改它,以便它也返回文件的索引?

【问题讨论】:

  • FWIW:可能有比查找索引更好的方法,并且“真正的问题”可能会得到解决——也许“更有效”——没有这样的操作。我很少索引到 IList。
  • @pst 哦,我确定有,但现在我有其他需要同步的列表,我正在使用索引来做到这一点。
  • 是的,我需要所有重复项的索引
  • 抱歉,当我说“文件的索引”而不是“最后一个/第一个文件的索引”时,我认为这部分很清楚

标签: c# list duplicates


【解决方案1】:

嗯,这是我对“查找重复名称及其索引”的回答。它可能不完全适合提出的问题,因为没有考虑baselineFilename - 但其他答案涵盖了这一点。 YMMV。

var names = new [] {"a", "a", "c", "b", "a", "b"};

var duplicatesWithIndices = names
    // Associate each name/value with an index
    .Select((Name, Index) => new { Name, Index })
    // Group according to name
    .GroupBy(x => x.Name)
    // Only care about Name -> {Index1, Index2, ..}
    .Select(xg => new {
        Name = xg.Key,
        Indices = xg.Select(x => x.Index)
    })
    // And groups with more than one index represent a duplicate key
    .Where(x => x.Indices.Count() > 1);

// Now, duplicatesWithIndices is typed like:
//   IEnumerable<{Name:string,Indices:IEnumerable<int>}>

// Let's say we print out the duplicates (the ToArray is for .NET 3.5):
foreach (var g in duplicatesWithIndices) {
    Console.WriteLine("Have duplicate " + g.Name + " with indices " +
        string.Join(",", g.Indices.ToArray()));
}

// The output for the above input is:
// > Have duplicate a with indices 0,1,4
// > Have duplicate b with indices 3,5

当然,提供的结果必须正确使用 - 这取决于最终必须做什么。

【讨论】:

  • 对索引进行分组正是我所需要的。谢谢!
  • +1 不错! - 我建议您投影到 KeyValuePair&lt;T, int[]&gt; 而不是匿名类型 - 这样可以使其成为扩展方法:public static IEnumerable&lt;KeyValuePair&lt;T, int[]&gt;&gt; GetDuplicates&lt;T&gt;(this IEnumerable&lt;T&gt; e) { return e.Select((value, index) =&gt; new {Index = index, Value = value}).GroupBy(x=&gt;x.Value).Select(xg =&gt; new KeyValuePair&lt;T, int[]&gt;(xg.Key, xg.Select(x =&gt; x.Index).ToArray())).Where(kv =&gt; kv.Value.Length &gt; 1); }(抱歉格式错误)
【解决方案2】:

您可以选择所有项目及其索引:

tempList = targetFileList.Select((item, index) => 
    new { Value = item, Index = index }).Where(x => x.Value == baselineFilename);

现在,您可以使用以下命令创建名称列表和相应索引:

var indexes = tempList.Select(x => x.Index).ToList();

以及价值观:

currentTargetFiles = tempList.Select(x => x.Value).ToList();

那么,indexes[0] 将保存currentTargetFiles[0] 的列表索引。

【讨论】:

  • where 子句导致错误:无法将类型“System.Collections.Generic.IEnumerable”隐式转换为“System.Collections.Generic.List”。存在显式转换(您是否缺少演员表?)
  • @user1985189 与其他答案一样,您必须对值/索引对做一些事情。如果此时只需要索引,附加.Select(x =&gt; x.Index).ToList() 将导致List&lt;int&gt;(仍然不是List&lt;string&gt;,但它应该显示这个想法)。
  • @pst 谢谢我感谢你所做的努力,但这个 linq 的东西让我难以接受。我想我要回到绘图板上,看看是否还有其他解决方法。不过,我会给你一堆赞成票!
  • @user1985189 LINQ 完全值得学习。当然,这需要一段时间(许多开发人员没有“函数式语言背景”),但是一旦制定了基本结构,它就可以用来大大简化许多常见任务。分组、过滤、交叉/连接、转换等.. 这是一个合格的 C# 开发人员的工具箱中的工具。 (仅仅因为有一个工具,并不意味着它应该被使用;但它应该是可用的,因为在某些情况下它是正确的工具。)
  • @pst 我确信它非常有价值。如果我最终从事涉及 C# 编程的工作,我一定会阅读它。 (现在我在一个为期 4 个月的 coop 学期,这是我第一次接触 C# - 以前只在学校学习过 Java,我们从未被介绍过 linq)
【解决方案3】:
int i = -1;
var currentTargetFiles = targetFileList.Select(x => new
                                                        {
                                                           Value = x,
                                                           Index = i++
                                                        })
                                       .Where(x => x.Value == baselineFilename);

【讨论】:

  • 我需要所有项目的索引。这产生了一个错误:无法将类型“System.Collections.Generic.IEnumerable”隐式转换为“System.Collections.Generic.List”。存在显式转换(您是否缺少演员表?)
  • @HighCore Select((x, index) =&gt; ..)
  • 好的,它现在可以编译,但是当我运行它时,我得到的值是一些无意义的胡言乱语: {System.Linq.Enumerable.WhereEnumerableIteratorf__AnonymousType0>} base {System.Linq .Enumerable.Iteratorf__AnonymousType0>}: {System.Linq.Enumerable.WhereEnumerableIteratorf__AnonymousType0>}
  • @user1985189:不可能返回包含字符串列表索引的List&lt;string&gt;。您可以使用它来生成匿名结构列表,然后从中创建两个列表。
  • @user1985189 发布您的完整代码。您在不正确的地方执行 ToString()。
【解决方案4】:

linq 是必需的吗?

传统的 for 循环和字典就可以了:

Dictionary<int, string> currentTargetFiles = new Dictionary<int, string>();
for (int i = 0; i < targetFileList.Count; ++i)
    if(targetFileList[i] == baselineFilename)
        currentTargetFiles.Add(i, targetFileList[i]);

附注:

刚刚意识到您正在比较一个确切的字符串 (item == baselineFilename)。

如果是这种情况,您甚至不需要为每个索引保留每个值(因为所有值都相同)。

List<int> currentTargetFilesIndices = new List<int>();
for (int i = 0; i < targetFileList.Count; ++i)
    if(targetFileList[i] == baselineFilename)
        currentTargetFiles.Add(i);

【讨论】:

  • 不,我不需要 linq,只是当我在 Google 上搜索示例时,似乎每个人都在这样做! tbh 我对 linq 一无所知,这可能很明显
猜你喜欢
  • 2011-09-24
  • 2011-06-02
  • 2022-01-20
  • 2020-01-07
  • 2019-01-25
  • 1970-01-01
  • 2017-12-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多