【问题标题】:Remove List Elements that appear more than Once In Place删除多次出现的列表元素
【发布时间】:2016-04-05 02:13:47
【问题描述】:

发布了一个类似的问题,但我没有代表在该线程中提出后续问题:(

那个问题和解决方案是HERE

如果我有一个包含多次出现的项目的列表,List.Distinct() 将删除重复项,但原始项仍将保留。如果我想删除多次出现的项目,包括原始项目,那么对 ORIGINAL 列表执行此操作的最有效方法是什么?

给定一个名为 oneTime 的列表: { 4, 5, 7, 3, 5, 4, 2, 4 }

所需的输出将是一次性的: { 7, 3, 2 }

感谢您的帮助!

----@enigmativity 的编辑和跟进问题,2016 年 4 月 8 日-----

这是我的脚本正在执行的伪版本。它是在 .NET3.5 上运行的 NinjaTrader 中完成的。

我将附上代码应该做什么的一般概念,我会附上实际的脚本,但除非使用 NinjaTrader,否则它可能没有用。

但本质上,有一个大的 z 环。每次通过时,都会将一系列数字添加到“LiTics”中。我不想打扰。然后我将该列表传递给函数,并返回一个只出现一次的值列表。然后我想每次通过循环看到这些数字。

它最初可以工作,但是在各种数据集上运行它,经过几次循环后,它开始报告多次出现的值。我不确定到底是为什么?

for(int z=1; z<=10000; z +=1)//Runs many times 
{ 
    if (BarsInProgress ==0 &&CurrentBar-oBarTF1>0 &&startScript )   //Some Condition
    {
        for(double k=Low[0]; k<=High[0]; k +=TickSize)  
        {   
            LiTics.Add(k);  
            //Adds a series of numbers to this list each time through z loop
            //This is original that I do not want to disturb
        }

        LiTZ.Clear();  //Display list to show me results Clear before populating
        LiTZ=GetTZone(LiTics); //function created in thread(below)
                               //Passing the undisturbed list that is modified on every loop
        foreach (double prime in LiTZ) { Print(Times[0] +",  " +prime);  }
        //Printing to see results   
    }

}//End of bigger 'z' loop

//Function created to get values that appear ONLY once
public List<double> GetTZone(List<double> sequence) 
{  
    var result =
        sequence
            .GroupBy(x => x)
            .Where(x => !x.Skip(1).Any())
            .Select(x => x.Key)
            .ToList();
    return result;

}

打印出来的图片和出了什么问题: http://i.stack.imgur.com/pXcdK.jpg

【问题讨论】:

  • 那么为什么不将结果分配给原始列表呢?

标签: c# linq list


【解决方案1】:

所以,如果你可以有一个新列表,那么这是最简单的方法:

var source = new List<int>() { 4, 5, 7, 3, 5, 4, 2, 4 };

var result =
    source
        .GroupBy(x => x)
        .Where(x => !x.Skip(1).Any())
        .Select(x => x.Key)
        .ToList();

这给出了:

{ 7, 3, 2 }

如果您想从原始来源中删除值,请执行以下操作:

var duplicates =
    new HashSet<int>(
        source
            .GroupBy(x => x)
            .Where(x => x.Skip(1).Any())
            .Select(x => x.Key));

source.RemoveAll(n => duplicates.Contains(n));

【讨论】:

  • 这个可以调用多少次有什么已知问题吗?我有一个函数,它通过从第一个示例创建一个新列表来执行上面的代码。它最初按预期工作,但在重复调用后,它开始返回多次出现的数字列表。
  • @pelt - 这应该每次都有效。你能展示一些失败的示例代码吗?
  • 附上上面的例子,我似乎遇到了一个标记为“编辑”的问题。显示列表中仅出现一次的值的脚本出现在循环中,每次递增都会将值添加到其中。每次我想显示出现一次的值列表时,稍后我将对这些值做一些事情。
  • @pelt - 我已经在你的代码中看到了一些奇怪的东西,但我无法运行它来真正测试它。您确实需要为我提供足够的代码来复制并粘贴到开发环境中并运行它。但是,我可以告诉您,我的代码不会导致您出现问题。你能发一个minimal reproducible example吗?
  • @pelt - 这里有一些开始的问题。 (1) 当您调用LiTZ=GetTZone(LiTics); 时,您正在为LiTZ 分配一个全新的列表,因此根本不需要先调用LiTZ.Clear()。变量k 有两种不同的用途。您正在使用 double 作为循环变量进行循环,这可能意味着您有舍入错误。
【解决方案2】:

我有两种选择,一种使用HashSet,另一种使用Linq

选项 1:

使用HashSet,循环遍历集合,如果不存在则插入,如果存在则移除。

HashSet<int> hash = new HashSet<int>();

foreach(var number in list)
{
    if(!hash.Contains(number)) hash.Add(number);
    else hash.Remove(number);               
}
list = hash.ToList();

选项 2:

简单的Linq, 将元素分组并过滤计数&gt;1

var list= list.GroupBy(g=>g)
    .Where(e=>e.Count()==1)
    .Select(g=>g.Key)
    .ToList();

使用HashSetLinq的性能增益,很明显,Linq(在这种情况下)需要多次迭代,而 HashSet 使用单次迭代并提供具有O(1) 访问权限的查找(用于添加/删除)。

Elapsed Time (Using Linq): 8808 Ticks
Elapsed Time (Using HashSet): 51 Ticks

工作Demo

【讨论】:

  • 作为 OP 我没有投反对票。我刚回来看到这个......我不知道谁投了反对票?
  • @pelt 很遗憾,人们在没有解释原因的情况下投票。
  • 我正在认真尝试了解其他答案与我的不同之处?这个答案被投票赞成。
  • 我没有看到演示与此问题相关。也许这就是反对票的来源?否则,虽然我没有测试您的解决方案,但有关效率的信息很受欢迎。
  • 这很可能被否决了,因为选项 1 根本不起作用。即使有三个 4 值,使用问题中的输入值也会产生 { 2, 4, 7, 3 }。删除4 并正确生成{ 2, 7, 3 }。问题是你的if/else,如果重复奇数次,它的效果就是将一个值视为唯一的。此外,该演示链接到不相关的 XML 解析代码。顺便说一句,Add() 返回一个 bool 指示该值是否已添加或已存在,因此您无需使用 if (!hash.Contains(number)) hash.Add(number); 进行条件添加。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多