检查集合c#中是不是存在元素

Posted

技术标签:

【中文标题】检查集合c#中是不是存在元素【英文标题】:Check for existence of an element in the collection c#检查集合c#中是否存在元素 【发布时间】:2021-05-05 04:12:29 【问题描述】:

我试图阻止在列表中添加 C# 中已存在的项目。下面的代码循环遍历数据表行。 如您所见, rows 的类型为 List<CubeReportRow>

数据表行确实包含重复项。我需要检查数据表中的 rowName 是否已经在 List<CubeReportRow> 类型的行对象中。请查看我在 foreach 循环中设置的条件。当我尝试按行名检查时,它说无法将字符串转换为 CubeReportRow 类型。如果我检查 if (!rows.Contains(row[0])),没有编译错误,但我不工作。如何检查它是否存在于 rows 集合中。

类 CubeReportRow

 public class CubeReportRow
    
        public string RowName  get; set; 
        public string RowParagraph  get; set; 
        public int ReportSection  get; set; 
    

C#方法

 public virtual IList<CubeReportRow> TransformResults(CubeReport report,DataTable dataTable)
        
            if (dataTable.Rows.Count == 0 || dataTable.Columns.Count == 0)
                return new List<CubeReportRow>();

            var rows = new List<CubeReportRow>();
            var columns = columnTransformer.GetColumns(dataTable);

            foreach (DataRow row in dataTable.Rows)
            
                
                var rowName = row[0].ToString();
                if (!rows.Contains(rowName))
                
                    var values =
                        cubeReportValueFactory.CreateCubeReportValuesForRow(dataTable, row, rowName, columns, report);

                    var reportRow = new CubeReportRow(row[3].ToString(), row[2].ToString(), row[1].ToString(), values);
                    rows.Add(reportRow);
                
            

            return rows;
        

【问题讨论】:

查看IEqualityComparer&lt;T&gt; 以获得您的CubeReportRow。具有相同的RowName 属性值是否具有 Equals 的语义 【参考方案1】:

您可以将Dictionary&lt;string, CubeReportRow&gt; 用于您的rows 变量,并使用ContainsKey 检查密钥(rowName)是否存在:

var rows = new Dictionary<string, CubeReportRow>();
if (!rows.ContainsKey(rowName))

    // ...
    rows.Add(rowName, reportRow);


// ...

return rows.Values.ToList();

【讨论】:

唯一的问题是在这种情况下您将获得后进,而使用! contains 您将获得先入。 如果你检查一个集合是否包含一个属性,那么第一个被添加,随后的则不被添加。如果你使用字典,多次添加相同的键会覆盖第一个值,所以你会得到最后一个。 @Zer0 好点,所以也许这毕竟是最好的解决方案:) @zaitsman 是的,我撤回了我的评论,因为TryAdd 没有价值就无法工作,如果密钥存在,OP 将跳过价值创造。我只是有肌肉记忆可以发现Contains,然后是Add。此答案还考虑了性能。 @zaitsman 逻辑相同 - 使用 ContainsKey 检查是否存在具有相同 rowName 的内容 - 如果不存在 - 添加新内容,因此结果应该相同。跨度> 【参考方案2】:

这不是一个真正的答案,因为我相信Guru Strons 的答案就足够了。

但是,有很多方法可以做到这一点,这将产生不同的性能和复杂性,具体取决于您的数据/重复率(并且不限于以下)。

字典

var rows = new Dictionary<string, CubeReportRow>();
foreach (var dataRow in _data)
   if (!rows.ContainsKey(dataRow.RowName))
      rows.Add(dataRow.RowName, dataRow);
return rows.Values.ToList();

哈希集

var hashSet = new HashSet<string>(_data.Length);
return _data.Where(x => hashSet.Add(x.RowName)).ToList();

分组方式

return _data.GroupBy(x => x.RowName).Select(x => x.First()).ToList();

IEqualityComparer

public class SomeComparer : IEqualityComparer<CubeReportRow> 
   public bool Equals(CubeReportRow x, CubeReportRow y) 
      return x.RowName == y.RowName;
   
   public int GetHashCode(CubeReportRow obj) 
      return obj.RowName.GetHashCode();
   


...

return _data.Distinct(new SomeComparer()).ToList();

基准测试

配置

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.746 (2004/?/20H1)
Intel Core i7-7700 CPU 3.60GHz (Kaby Lake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=5.0.102
  [Host]        : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT
  .NET Core 5.0 : .NET Core 5.0.2 (CoreCLR 5.0.220.61120, CoreFX 5.0.220.61120), X64 RyuJIT

Job=.NET Core 5.0  Runtime=.NET Core 5.0

结果

Method Mean Error StdDev
Dictionary 205.3 us 4.06 us 5.69 us
HashSet 237.6 us 4.73 us 10.19 us
Distinct 299.4 us 5.24 us 4.90 us
GroupBy 451.3 us 5.28 us 4.68 us

完整的测试代码

[SimpleJob(RuntimeMoniker.NetCoreApp50)]
public class Test

   private CubeReportRow[] _data;


   public class CubeReportRow
   
      public string RowName  get; set; 
      public string RowParagraph  get; set; 
      public int ReportSection  get; set; 
   

   [GlobalSetup]
   public void Setup()
   
      var r = new Random(32);
      _data = new CubeReportRow[10000];
      for (int i = 0; i < 10000; i++)
         _data[i] = new CubeReportRow() RowName = r.Next(100).ToString();

   

   [Benchmark]
   public List<CubeReportRow> Dictionary()
   
      var rows = new Dictionary<string, CubeReportRow>();
      foreach (var dataRow in _data)
         if (!rows.ContainsKey(dataRow.RowName))
            rows.Add(dataRow.RowName, dataRow);
      return rows.Values.ToList();
   

   [Benchmark]
   public List<CubeReportRow> HashSet()
   
      var hashSet = new HashSet<string>(_data.Length);
      return _data.Where(x => hashSet.Add(x.RowName)).ToList();

   

   public class SomeComparer : IEqualityComparer<CubeReportRow>
   
      public bool Equals(CubeReportRow x, CubeReportRow y)
      
         return x.RowName == y.RowName;
      

      public int GetHashCode(CubeReportRow obj)
      
         return obj.RowName.GetHashCode();
      
   

   [Benchmark]
   public List<CubeReportRow> Distinct()
   
      return _data.Distinct(new SomeComparer()).ToList();

   

   [Benchmark]
   public List<CubeReportRow> GroupBy()
   
      return _data.GroupBy(x => x.RowName).Select(x => x.First()).ToList();

   

注意:如果您对性能感兴趣,请使用真实数据自行运行这些基准测试。

【讨论】:

【参考方案3】:

LINQ 非常适合这一点(就易于阅读的代码而言)

在文件顶部: using System.Linq;

然后: if (!rows.Any(r =&gt; r.RowName == rowName))(替换if (!rows.Contains(rowName))

【讨论】:

@GuruStron 不:docs.microsoft.com/en-us/dotnet/api/… 他的返回类型是IList,他没有提到LINQ。无论如何,您链接的 Contains 需要传入一个完整的对象,并将使用 .Equals() 进行评估,但 OP 需要的是通过他的数据行中的第一列进行比较。 我在 r[0] 行遇到错误。无法将索引应用于 CubeReportRow 类型的表达式 @Tom 道歉,改成r.RowName(见我的编辑) 如果 (!rows.Any(r => r.RowName == rowName)) 可能是这样的

以上是关于检查集合c#中是不是存在元素的主要内容,如果未能解决你的问题,请参考以下文章

检查元素是不是属于集合的算法

C#数据结构

C#日常C#泛型集合Dictionary<TKey, TValue>使用方法及泛型集合检查是否存在Key值

C#日常C#泛型集合Dictionary<TKey, TValue>使用方法及泛型集合检查是否存在Key值

如何检查MongoDB本机nodejs驱动程序中是不是存在集合?

Swift 5 Firestore:检查数据库中是不是存在集合