【问题标题】:Loop to check for duplicate strings循环检查重复的字符串
【发布时间】:2018-01-30 14:50:40
【问题描述】:

我想创建一个循环来检查重复的标题列表。

我目前有这个:

var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));

foreach (var x in productTitles)
{
    var title = x.Text;

    productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));

    foreach (var y in productTitles.Skip(productTitles.IndexOf(x) + 1))
    {
        if (title == y.Text)
        {
            Assert.Fail("Found duplicate product in the table");
        }
    }
}

但这是我从数组中跳过的项目以进行下一个循环,因此项目 2 从不检查它是否与项目 1 相同,它直接移动到项目 3。

我的印象是,skip 只是传递了您传入的索引,而不是从列表中删除它。

【问题讨论】:

  • 我相信skip会跳过'N'个元素。因此,如果您想忽略第一个元素,那么您 Skip(1)... 等等。
  • GroupBy 将在一行中完成所有操作。如果可能的话,使用 Linq
  • 我希望能够跳过第一个循环中的元素,这样它就不会对自己进行检查是否有意义。所以第 1 项检查除 1 之外的所有元素,第 2 项检查第 1、3、4、5 项等
  • 跳过不删除..
  • 使用 GroupBy 并过滤 where group.Count() > 1. Skip 从字面上跳过给定数量的记录。它不会跳过索引位置。而且,谁在乎呢,因为如果你按顺序比较每个项目,你永远不需要回溯。如果您检查 1 与 2,则无需重新检查 2 与 1。3、4 等也是如此。

标签: c# loops foreach


【解决方案1】:

你可以使用GroupBy:

var anyDuplicates = SeleniumContext
    .Driver
    .FindElements(By.XPath(ComparisonTableElements.ProductTitle))
    .GroupBy(p => p.Text, p => p)
    .Any(g => g.Count() > 1);

Assert.That(anyDuplicates, Is.False);

Distinct:

var productTitles = SeleniumContext
    .Driver
    .FindElements(By.XPath(ComparisonTableElements.ProductTitle))
    .Select(p => p.Text)
    .ToArray();

var distinctProductTitles = productTitles.Distinct().ToArray();

Assert.AreEqual(productTitles.Length, distinctProductTitles.Length);

或者,如果在不计算所有副本的情况下找到第一个副本就足够了,那么最好使用HashSet<T>

var titles = new HashSet<string>();

foreach (var title in SeleniumContext
    .Driver
    .FindElements(By.XPath(ComparisonTableElements.ProductTitle))
    .Select(p => p.Text))
{
    if (!titles.Add(title))
    {
        Assert.Fail("Found duplicate product in the table");
    }
}

就计算复杂度 (O(n)) 而言,所有方法都比您建议的方法 (O(n2)) 更好。

【讨论】:

    【解决方案2】:

    您不需要循环。只需使用Where() 函数查找所有相同的标题,如果有多个,则它们是重复的:

    var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle));
    
    foreach(var x in productTitles) {
      if (productTitles.Where(y => x.Text == y.Text).Count() > 1) {
        Assert.Fail("Found duplicate product in the table");
      }
    }
    

    【讨论】:

      【解决方案3】:

      我会尝试一种稍微不同的方法,因为您只需要检查一维数组中的重复项。

      您只需检查数组/集合中的前一个元素和下一个元素,因此使用Linq 遍历所有项目似乎有点不必要。

      这里有一段代码可以更好地理解:

      var productTitles = SeleniumContext.Driver.FindElements(By.XPath(ComparisonTableElements.ProductTitle))
      
      for ( int i = 0; i < productionTitles.Length; i++ )
      {
          var currentObject = productionTitles[i];
          for ( int j = i + 1; j < productionTitles.Length; j++ )
          {
              if ( currentObject.Title == productionTitles[j].Title )
              {
                  // here's your duplicate
              }
          }
      }
      

      由于您已检查索引 0 处的项目与索引 3 处的项目不同,因此当您位于索引 3 时无需再次检查。项目将保持不变。

      【讨论】:

        【解决方案4】:

        Skip(IEnumerable, n) 方法返回一个不“包含”它所调用的 IEnumerable 的第 n 个元素的 IEnumerable。

        我也不知道这会导致什么样的行为,但我不会将新的 IEnumerable 分配给执行 foreach 的变量。

        这是另一种可能的 LINQ 解决方案:

        int i = 0;
        foreach (var x in productTitles)
        {
            var possibleDuplicate = productTitles.Skip(i++).Find((y) => y.title == x.title);
        
            //if possibleDuplicate is not default value of type
                //do stuff here
        }
        

        这是不言而喻的,但最适合您的解决方案将取决于您要做什么。另外,我认为Skip 方法调用麻烦多于其价值,因为我很确定它肯定会使搜索效率降低。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多