简体   繁体   English

基于两个属性从列表中选择不同值的最快方法

[英]Fastest way to select distinct values from list based on two properties

I have a this list: 我有一个清单:

List<myobject> list= new List<myobject>();

list.Add(new myobject{name="n1",recordNumber=1}); 
list.Add(new myobject{name="n2",recordNumber=2}); 
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});

I'm looking for the fastest way to select distinct objects based on recordNumber, but if there is more than one object with same recordNumber(here recordNumber=3), I want to select object base on its name.(the name provided by paramater) 我正在寻找基于recordNumber选择不同对象的最快方法,但是如果有多个具有相同recordNumber(此处recordNumber = 3)的对象,我想根据其名称选择对象。(名称由paramater提供) )

thanks 谢谢

It looks like you are really after something like: 看起来您确实是在追求类似的东西:

Dictionary<int, List<myobject>> myDataStructure;

That allows you to quickly retrieve by record number. 这样您就可以按记录号快速检索。 If the List<myobject> with that dictionary key contains more than one entry, you can then use the name to select the correct one. 如果具有该字典键的List<myobject>包含多个条目,则可以使用该名称选择正确的条目。

Note that if your list is not terribly long, an O(n) check that just scans the list checking for the recordNumber and name may be fast enough , in the sense that other things happening in your program could obscure the list lookup cost. 请注意,如果列表不是很长,那么仅扫描列表以检查recordNumber和name的O(n)检查可能足够快 ,因为程序中发生的其他事情可能会掩盖列表查找的费用。 Consider that possibility before over-optimizing lookup times. 在过度优化查找时间之前,请考虑这种可能性。

Here's the LINQ way of doing this: 这是执行此操作的LINQ方法:

Func<IEnumerable<myobject>, string, IEnumerable<myobject>> getDistinct =
    (ms, n) =>
        ms
            .ToLookup(x => x.recordNumber)
            .Select(xs => xs.Skip(1).Any()
                ? xs.Where(x => x.name == n).Take(1)
                : xs)
            .SelectMany(x => x)
            .ToArray();

I just tested this with a 1,000,000 randomly created myobject list and it produced the result in 106ms. 我刚刚用1,000,000个随机创建的myobject列表进行了测试,并在106ms内产生了结果。 That should be fast enough for most situations. 对于大多数情况,这应该足够快。

Are you looking for 你在找吗

class Program
    {
        static void Main(string[] args)
        {
            List<myobject> list = new List<myobject>();

            list.Add(new myobject { name = "n1", recordNumber = 1 });
            list.Add(new myobject { name = "n2", recordNumber = 2 });
            list.Add(new myobject { name = "n3", recordNumber = 3 });
            list.Add(new myobject { name = "n4", recordNumber = 3 });

            //Generates Row Number on the fly
            var withRowNumbers = list 
                    .Select((x, index) => new 
                            {
                                Name = x.name,
                                RecordNumber = x.recordNumber,
                                RowNumber = index + 1
                            }).ToList();

            //Generates Row Number with Partition by clause
            var withRowNumbersPartitionBy = withRowNumbers
                    .OrderBy(x => x.RowNumber)
                    .GroupBy(x => x.RecordNumber)
                    .Select(g => new { g, count = g.Count() })
                    .SelectMany(t => t.g.Select(b => b)
                    .Zip(Enumerable.Range(1, t.count), (j, i) => new { Rn = i, j.RecordNumber, j.Name}))
                    .Where(i=>i.Rn == 1)
                    .ToList();
            //print the result
            withRowNumbersPartitionBy.ToList().ForEach(i => Console.WriteLine("Name =  {0}   RecordNumber = {1}", i.Name, i.RecordNumber));

            Console.ReadKey();
        }
    }

    class myobject
    {
        public int recordNumber { get; set; }
        public string name { get; set; }
    }

Result: 结果:

Name =  n1   RecordNumber = 1
Name =  n2   RecordNumber = 2
Name =  n3   RecordNumber = 3

Are you looking for a method to do this? 您是否正在寻找一种方法来做到这一点?

List<myobject> list= new List<myobject>();

list.Add(new myobject{name="n1",recordNumber=1}); 
list.Add(new myobject{name="n2",recordNumber=2}); 
list.Add(new myobject{name="n3",recordNumber=3});
list.Add(new myobject{name="n4",recordNumber=3});

public myobject Find(int recordNumber, string name)
{
    var matches = list.Where(l => l.recordNumber == recordNumber);

    if (matches.Count() == 1)
        return matches.Single();

    else return matches.Single(m => m.name == name);
}

This will - of course - break if there are multiple matches, or zero matches. 如果有多个匹配项或零个匹配项,这当然会中断。 You need to write your own edge cases and error handling! 您需要编写自己的优势案例和错误处理方法!

If the name and recordNumber combination is guaranteed to be unique then you can always use Hashset . 如果名称和recordNumber的组合保证是唯一的,则可以始终使用Hashset

You can then use RecordNumber and Name to generate the HashCode by using a method described here . 然后,可以通过使用此处描述的方法,使用RecordNumber和Name生成HashCode。

class myobject 
{

     //override GetHashCode
     public override int GetHashCode()
     {
        unchecked // Overflow is fine, just wrap
        {
           int hash = 17;
           // Suitable nullity checks etc, of course :)
           hash = hash * 23 + recordNumber.GetHashCode();
           hash = hash * 23 + name.GetHashCode();
           return hash;
         }
     }
     //override Equals      
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM