ความแตกต่าง () กับแลมบ์ดา?


746

ใช่ฉันมีจำนวนมากและต้องการรับค่าที่แตกต่างจากมัน

ใช้มีแน่นอนวิธีขยายที่เรียกว่าSystem.Linq Distinctในกรณีง่าย ๆ มันสามารถใช้โดยไม่มีพารามิเตอร์เช่น:

var distinctValues = myStringList.Distinct();

ดีและดี แต่ถ้าฉันมีจำนวนวัตถุที่ฉันต้องระบุความเท่าเทียมกันเกินพิกัดที่มีอยู่เท่านั้นคือ:

var distinctValues = myCustomerList.Distinct(someEqualityComparer);

อาร์กิวเมนต์เท่าเทียมกัน Comparer IEqualityComparer<T>ต้องเป็นตัวอย่างของ แน่นอนว่าฉันสามารถทำสิ่งนี้ได้ แต่มันค่อนข้างละเอียดและดูดี

สิ่งที่ฉันคาดหวังก็คือโอเวอร์โหลดที่จะเอาแลมบ์ดาพูด Func <T, T, bool>:

var distinctValues
    = myCustomerList.Distinct((c1, c2) => c1.CustomerId == c2.CustomerId);

มีใครทราบบ้างไหมว่าส่วนขยายดังกล่าวมีอยู่หรือไม่ หรือฉันกำลังพลาดอะไรอยู่?

หรือมีวิธีระบุ IEqualityComparer inline (ทำให้ลำบากใจฉัน) หรือไม่

ปรับปรุง

ฉันพบคำตอบของ Anders Hejlsberg ต่อการโพสต์ในฟอรัม MSDN ในหัวข้อนี้ เขาพูดว่า:

ปัญหาที่คุณจะพบคือเมื่อวัตถุสองตัวเปรียบเทียบกันเท่ากับพวกเขาจะต้องมีค่าส่งคืน GetHashCode เดียวกัน (หรือตารางแฮชที่ใช้ภายในโดย Distinct จะทำงานไม่ถูกต้อง) เราใช้ IEqualityComparer เพราะมันใช้งานร่วมกับ Equals และ GetHashCode ได้ในอินเตอร์เฟสเดียว

ฉันคิดว่าเหมาะสมแล้ว


2
ดูstackoverflow.com/questions/1183403/… สำหรับวิธีแก้ปัญหาโดยใช้ GroupBy

17
ขอบคุณสำหรับการอัพเดต Anders Hejlsberg!
Tor Haugen

ไม่มันไม่สมเหตุสมผล - วัตถุสองชนิดที่มีค่าเหมือนกันจะส่งคืนแฮชโค้ดที่ต่างกันสองวิธีได้อย่างไร
GY

มันอาจช่วยให้ - การแก้ปัญหาสำหรับ.Distinct(new KeyEqualityComparer<Customer,string>(c1 => c1.CustomerId)), และอธิบายว่าทำไม GetHashCode () เป็นสิ่งสำคัญในการทำงานอย่างถูกต้อง
marbel82

ซ้ำซ้อนที่เกี่ยวข้อง / เป็นไปได้ของ: LINQ's Distinct () ในสถานที่เฉพาะ
Marc.2377

คำตอบ:


1028
IEnumerable<Customer> filteredList = originalList
  .GroupBy(customer => customer.CustomerId)
  .Select(group => group.First());

12
ยอดเยี่ยม นี่เป็นเรื่องง่ายที่จะสรุปในวิธีการขยายเช่นกันDistinctBy(หรือแม้กระทั่งDistinctเนื่องจากลายเซ็นจะไม่ซ้ำกัน)
Tomas Aschan

1
ไม่ได้ผลสำหรับฉัน! <วิธีการ 'แรก' สามารถใช้เป็นการดำเนินการค้นหาขั้นสุดท้ายเท่านั้น ลองใช้วิธีการ 'FirstOrDefault' ในอินสแตนซ์นี้แทน> แม้ว่าฉันจะลอง 'FirstOrDefault' แล้ว แต่ก็ไม่ได้ผล
JatSing

63
@ TorHaugen: โปรดระวังว่ามีค่าใช้จ่ายในการสร้างกลุ่มเหล่านั้นทั้งหมด สิ่งนี้ไม่สามารถสตรีมอินพุตและจะสิ้นสุดการบัฟเฟอร์ข้อมูลทั้งหมดก่อนที่จะส่งคืนอะไร ที่อาจจะไม่เกี่ยวข้องกับสถานการณ์ของคุณแน่นอน แต่ผมชอบความสง่างามของ DistinctBy :)
จอนสกีต

2
@JonSkeet: สิ่งนี้ดีพอสำหรับตัวแปลง VB.NET ที่ไม่ต้องการนำเข้าไลบรารีเพิ่มเติมสำหรับคุณลักษณะเดียว หากไม่มี ASync CTP, VB.NET จะไม่สนับสนุนyieldคำสั่งดังกล่าวดังนั้นจึงเป็นไปไม่ได้ในทางเทคนิค ขอบคุณสำหรับคำตอบของคุณ ฉันจะใช้เมื่อเข้ารหัสใน C # ;-)
Alex Essilfie

2
@BenGripka: นั่นไม่เหมือนกันเลย มันจะช่วยให้คุณรหัสลูกค้า ฉันต้องการของลูกค้าทั้ง :)
ryanman

496

ดูเหมือนว่าผมชอบคุณต้องการDistinctByจากMoreLINQ จากนั้นคุณสามารถเขียน:

var distinctValues = myCustomerList.DistinctBy(c => c.CustomerId);

ต่อไปนี้เป็นเวอร์ชันแบบตัดลงของDistinctBy(ไม่มีการตรวจสอบความผิดพลาดและไม่มีตัวเลือกในการระบุเครื่องมือเปรียบเทียบคีย์ของคุณเอง):

public static IEnumerable<TSource> DistinctBy<TSource, TKey>
     (this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
    HashSet<TKey> knownKeys = new HashSet<TKey>();
    foreach (TSource element in source)
    {
        if (knownKeys.Add(keySelector(element)))
        {
            yield return element;
        }
    }
}

14
ฉันรู้ว่าคำตอบที่ดีที่สุดจะถูกโพสต์โดย Jon Skeet เพียงแค่อ่านชื่อของโพสต์ หากมันเกี่ยวข้องกับ LINQ Skeet ก็เป็นคนของคุณ อ่าน 'C # In Depth' เพื่อรับความรู้ linq เหมือนพระเจ้า
nocarrier

2
คำตอบที่ดี !!! นอกจากนี้สำหรับ VB_Complainers ทั้งหมดเกี่ยวกับyield+ lib พิเศษ foreach สามารถเขียนใหม่ได้อีกด้วยreturn source.Where(element => knownKeys.Add(keySelector(element)));
denis morozov

5
@ sudhAnsu63 นี่เป็นข้อ จำกัด ของ LinqToSql (และผู้ให้บริการ linq อื่น ๆ ) เจตนาของ LinqToX คือการแปล C # แลมบ์ดาของคุณเป็นบริบทดั้งเดิมของ X นั่นคือ LinqToSql แปลง C # ของคุณเป็น SQL และดำเนินการคำสั่งนั้นอย่างเป็นไปได้ ซึ่งหมายความว่าวิธีการใด ๆ ที่อยู่ใน C # จะไม่สามารถ "ส่งผ่าน" linqProvider หากไม่มีวิธีการแสดงใน SQL (หรือผู้ให้บริการ linq ใด ๆ ที่คุณใช้) ฉันเห็นสิ่งนี้ในวิธีการขยายการแปลงวัตถุข้อมูลเพื่อดูแบบจำลอง คุณสามารถหลีกเลี่ยงสิ่งนี้ได้โดย "materializing" แบบสอบถามเรียก ToList () ก่อน DistinctBy ()
Michael Blackburn

1
และเมื่อใดก็ตามที่ฉันกลับมาที่คำถามนี้ฉันก็ยังสงสัยอยู่ว่าทำไมพวกเขาถึงไม่ยอมรับอย่างน้อย MoreLinq ใน BCL
Shimmy Weitzhandler

2
@Shimmy: ฉันยินดีอย่างแน่นอนว่า ... ฉันไม่แน่ใจว่าความเป็นไปได้คืออะไร ฉันสามารถเลี้ยงดูได้ใน. NET Foundation แม้ว่า ...
Jon Skeet

39

เพื่อตัดสิ่งขึ้น ผมคิดว่าส่วนใหญ่ของคนที่มาที่นี่เหมือนผมต้องการที่ง่ายที่สุดในการแก้ปัญหาที่เป็นไปได้โดยไม่ต้องใช้ห้องสมุดใด ๆและมีความเป็นไปได้ที่ดีที่สุดประสิทธิภาพ

(กลุ่มที่ได้รับการยอมรับโดยวิธีการสำหรับฉันฉันคิดว่ามันเกินความสามารถในแง่ของประสิทธิภาพ)

นี่คือวิธีการขยายอย่างง่ายโดยใช้ส่วนต่อประสานIEqualityComparerซึ่งทำงานได้กับค่า Null ด้วย

การใช้งาน:

var filtered = taskList.DistinctBy(t => t.TaskExternalId).ToArray();

รหัสวิธีการขยาย

public static class LinqExtensions
{
    public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> items, Func<T, TKey> property)
    {
        GeneralPropertyComparer<T, TKey> comparer = new GeneralPropertyComparer<T,TKey>(property);
        return items.Distinct(comparer);
    }   
}
public class GeneralPropertyComparer<T,TKey> : IEqualityComparer<T>
{
    private Func<T, TKey> expr { get; set; }
    public GeneralPropertyComparer (Func<T, TKey> expr)
    {
        this.expr = expr;
    }
    public bool Equals(T left, T right)
    {
        var leftProp = expr.Invoke(left);
        var rightProp = expr.Invoke(right);
        if (leftProp == null && rightProp == null)
            return true;
        else if (leftProp == null ^ rightProp == null)
            return false;
        else
            return leftProp.Equals(rightProp);
    }
    public int GetHashCode(T obj)
    {
        var prop = expr.Invoke(obj);
        return (prop==null)? 0:prop.GetHashCode();
    }
}

19

ไม่มีวิธีการขยายดังกล่าวเกินสำหรับสิ่งนี้ ฉันพบสิ่งนี้ทำให้หงุดหงิดในอดีตและเช่นนี้ฉันมักจะเขียนชั้นผู้ช่วยเพื่อจัดการกับปัญหานี้ เป้าหมายของเราคือการแปลงไป Func<T,T,bool>IEqualityComparer<T,T>

ตัวอย่าง

public class EqualityFactory {
  private sealed class Impl<T> : IEqualityComparer<T,T> {
    private Func<T,T,bool> m_del;
    private IEqualityComparer<T> m_comp;
    public Impl(Func<T,T,bool> del) { 
      m_del = del;
      m_comp = EqualityComparer<T>.Default;
    }
    public bool Equals(T left, T right) {
      return m_del(left, right);
    } 
    public int GetHashCode(T value) {
      return m_comp.GetHashCode(value);
    }
  }
  public static IEqualityComparer<T,T> Create<T>(Func<T,T,bool> del) {
    return new Impl<T>(del);
  }
}

สิ่งนี้ช่วยให้คุณสามารถเขียนสิ่งต่อไปนี้

var distinctValues = myCustomerList
  .Distinct(EqualityFactory.Create((c1, c2) => c1.CustomerId == c2.CustomerId));

8
ที่มีการใช้รหัสแฮชที่น่ารังเกียจ ง่ายกว่าในการสร้างIEqualityComparer<T>จากการฉายภาพ: stackoverflow.com/questions/188120/…
Jon Skeet

7
(เพื่ออธิบายความคิดเห็นของฉันเกี่ยวกับรหัสแฮช - มันง่ายมากที่รหัสนี้จะลงท้ายด้วย Equals (x, y) == จริง แต่ GetHashCode (x)! = GetHashCode (y) ซึ่งโดยทั่วไปแล้วจะแบ่งอะไรเช่น hashtable .)
Jon Skeet

ฉันเห็นด้วยกับการคัดค้านรหัสแฮช ถึงกระนั้น +1 สำหรับรูปแบบ
Tor Haugen

@ จอนใช่ฉันยอมรับการใช้งานดั้งเดิมของ GetHashcode น้อยกว่าที่ดีที่สุด (เป็นคนขี้เกียจ) ฉันเปลี่ยนไปใช้เป็นหลักตอนนี้ EqualityComparer <T> .Default.GetHashcode () ซึ่งเป็นมาตรฐานที่ค่อนข้างเล็กน้อย แม้ว่าที่จริงแล้วสิ่งเดียวที่รับประกันว่าจะใช้งาน GetHashcode ในสถานการณ์นี้คือการคืนค่าคงที่ ฆ่าการค้นหา hashtable แต่รับประกันได้ว่าถูกต้องตามหน้าที่
JaredPar

1
@ JaredPar: แน่นอน รหัสแฮชจะต้องสอดคล้องกับฟังก์ชั่นความเท่าเทียมกันที่คุณใช้ซึ่งอาจไม่ใช่ค่าเริ่มต้นมิฉะนั้นคุณจะไม่ต้องกังวล :) นั่นเป็นเหตุผลที่ฉันชอบใช้การฉายภาพ - คุณสามารถรับทั้งความเท่าเทียมและแฮชที่สมเหตุสมผลได้ รหัสด้วยวิธีนั้น นอกจากนี้ยังทำให้รหัสการโทรมีความซ้ำซ้อนน้อยลง ยอมรับว่าการทำงานเฉพาะในกรณีที่คุณต้องการให้ฉายเดียวกันสองครั้ง แต่เป็นกรณีที่ผมเคยเห็นในทางปฏิบัติทุก :)
จอนสกีต

18

วิธีแก้ปัญหาชวเลข

myCustomerList.GroupBy(c => c.CustomerId, (key, c) => c.FirstOrDefault());

1
คุณสามารถเพิ่มคำอธิบายว่าทำไมสิ่งนี้ถึงได้รับการปรับปรุง
Keith Pinson

มันใช้งานได้จริงสำหรับฉันเมื่อคอนราดไม่ได้
neoscribe

13

สิ่งนี้จะทำในสิ่งที่คุณต้องการ แต่ฉันไม่รู้เกี่ยวกับประสิทธิภาพ:

var distinctValues =
    from cust in myCustomerList
    group cust by cust.CustomerId
    into gcust
    select gcust.First();

อย่างน้อยก็ไม่ได้ verbose


12

ต่อไปนี้เป็นวิธีการขยายอย่างง่ายที่ทำในสิ่งที่ฉันต้องการ ...

public static class EnumerableExtensions
{
    public static IEnumerable<TKey> Distinct<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector)
    {
        return source.GroupBy(selector).Select(x => x.Key);
    }
}

มันเป็นความอัปยศที่พวกเขาไม่ได้อบวิธีที่แตกต่างเช่นนี้ในกรอบ แต่เฮ้โฮ


นี่เป็นทางออกที่ดีที่สุดโดยไม่ต้องเพิ่ม morelinq ของไลบรารี
toddmo

แต่ผมต้องเปลี่ยนx.Keyไปx.First()และเปลี่ยนค่าที่ส่งกลับไปIEnumerable<T>
toddmo

@toddmo ขอบคุณสำหรับข้อเสนอแนะ :-) ใช่เสียงตรรกะ ... ฉันจะอัปเดตคำตอบหลังจากตรวจสอบเพิ่มเติม
David Kirkland

1
มันไม่เคยสายเกินไปที่จะบอกว่าขอบคุณสำหรับการแก้ปัญหาที่ง่ายและสะอาด
อาลี

4

สิ่งที่ฉันใช้ซึ่งทำงานได้ดีสำหรับฉัน

/// <summary>
/// A class to wrap the IEqualityComparer interface into matching functions for simple implementation
/// </summary>
/// <typeparam name="T">The type of object to be compared</typeparam>
public class MyIEqualityComparer<T> : IEqualityComparer<T>
{
    /// <summary>
    /// Create a new comparer based on the given Equals and GetHashCode methods
    /// </summary>
    /// <param name="equals">The method to compute equals of two T instances</param>
    /// <param name="getHashCode">The method to compute a hashcode for a T instance</param>
    public MyIEqualityComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
    {
        if (equals == null)
            throw new ArgumentNullException("equals", "Equals parameter is required for all MyIEqualityComparer instances");
        EqualsMethod = equals;
        GetHashCodeMethod = getHashCode;
    }
    /// <summary>
    /// Gets the method used to compute equals
    /// </summary>
    public Func<T, T, bool> EqualsMethod { get; private set; }
    /// <summary>
    /// Gets the method used to compute a hash code
    /// </summary>
    public Func<T, int> GetHashCodeMethod { get; private set; }

    bool IEqualityComparer<T>.Equals(T x, T y)
    {
        return EqualsMethod(x, y);
    }

    int IEqualityComparer<T>.GetHashCode(T obj)
    {
        if (GetHashCodeMethod == null)
            return obj.GetHashCode();
        return GetHashCodeMethod(obj);
    }
}

@Mukus ฉันไม่แน่ใจว่าทำไมคุณถามเกี่ยวกับชื่อคลาสที่นี่ ฉันจำเป็นต้องตั้งชื่อคลาสเพื่อที่จะใช้ IEqualityComparer ดังนั้นฉันจึงนำหน้า My
Kleinux

4

โซลูชันทั้งหมดที่ฉันเห็นที่นี่ขึ้นอยู่กับการเลือกเขตข้อมูลที่เปรียบเทียบได้แล้ว หากเราต้องการเปรียบเทียบในวิธีที่แตกต่างกัน แต่วิธีนี้ดูเหมือนว่าจะใช้งานได้ทั่วไปสำหรับบางสิ่งเช่น:

somedoubles.Distinct(new LambdaComparer<double>((x, y) => Math.Abs(x - y) < double.Epsilon)).Count()

LambdaComparer คืออะไรคุณนำเข้าจากที่ไหน
Patrick Graham

@PatrickGraham เชื่อมโยงกับคำตอบ: brendan.enrick.com/post/…
Dmitry Ledentsov

3

ใช้วิธีอื่น:

var distinctValues = myCustomerList.
Select(x => x._myCaustomerProperty).Distinct();

ลำดับที่ส่งกลับองค์ประกอบที่แตกต่างเปรียบเทียบพวกเขาตามคุณสมบัติ '_myCaustomerProperty'


1
มาที่นี่เพื่อพูดสิ่งนี้ นี่ควรเป็นคำตอบที่ได้รับการยอมรับ
Still.Tony

5
ไม่นี่ไม่ใช่คำตอบที่ได้รับการยอมรับเว้นแต่ว่าสิ่งที่คุณต้องการคือค่าที่แตกต่างของคุณสมบัติที่กำหนดเอง คำถาม OP ทั่วไปคือวิธีคืนวัตถุที่แตกต่างโดยยึดตามคุณสมบัติเฉพาะของวัตถุ
โทโมะ

2

คุณสามารถใช้InlineComparer

public class InlineComparer<T> : IEqualityComparer<T>
{
    //private readonly Func<T, T, bool> equalsMethod;
    //private readonly Func<T, int> getHashCodeMethod;
    public Func<T, T, bool> EqualsMethod { get; private set; }
    public Func<T, int> GetHashCodeMethod { get; private set; }

    public InlineComparer(Func<T, T, bool> equals, Func<T, int> hashCode)
    {
        if (equals == null) throw new ArgumentNullException("equals", "Equals parameter is required for all InlineComparer instances");
        EqualsMethod = equals;
        GetHashCodeMethod = hashCode;
    }

    public bool Equals(T x, T y)
    {
        return EqualsMethod(x, y);
    }

    public int GetHashCode(T obj)
    {
        if (GetHashCodeMethod == null) return obj.GetHashCode();
        return GetHashCodeMethod(obj);
    }
}

ตัวอย่างการใช้งาน :

  var comparer = new InlineComparer<DetalleLog>((i1, i2) => i1.PeticionEV == i2.PeticionEV && i1.Etiqueta == i2.Etiqueta, i => i.PeticionEV.GetHashCode() + i.Etiqueta.GetHashCode());
  var peticionesEV = listaLogs.Distinct(comparer).ToList();
  Assert.IsNotNull(peticionesEV);
  Assert.AreNotEqual(0, peticionesEV.Count);

แหล่งที่มา: https://stackoverflow.com/a/5969691/206730
การใช้ IEqualityComparer สำหรับยูเนี่ยน
ฉันสามารถระบุอินไลน์การเปรียบเทียบแบบชัดเจนของฉันได้หรือไม่


2

คุณสามารถใช้ LambdaEqualityComparer:

var distinctValues
    = myCustomerList.Distinct(new LambdaEqualityComparer<OurType>((c1, c2) => c1.CustomerId == c2.CustomerId));


public class LambdaEqualityComparer<T> : IEqualityComparer<T>
    {
        public LambdaEqualityComparer(Func<T, T, bool> equalsFunction)
        {
            _equalsFunction = equalsFunction;
        }

        public bool Equals(T x, T y)
        {
            return _equalsFunction(x, y);
        }

        public int GetHashCode(T obj)
        {
            return obj.GetHashCode();
        }

        private readonly Func<T, T, bool> _equalsFunction;
    }

1

วิธีที่ยุ่งยากในการทำเช่นนี้คือใช้Aggregate()ส่วนขยายโดยใช้พจนานุกรมเป็นตัวสะสมพร้อมกับค่าคุณสมบัติคีย์เป็นคีย์:

var customers = new List<Customer>();

var distincts = customers.Aggregate(new Dictionary<int, Customer>(), 
                                    (d, e) => { d[e.CustomerId] = e; return d; },
                                    d => d.Values);

และการแก้ปัญหาสไตล์ GroupByใช้ToLookup():

var distincts = customers.ToLookup(c => c.CustomerId).Select(g => g.First());

ดี แต่ทำไมไม่สร้างเพียงDictionary<int, Customer>แทน
ruffin

0

ฉันสมมติว่าคุณมี IEnumerable และในตัวอย่างผู้รับมอบสิทธิ์ของคุณคุณต้องการให้ c1 และ c2 อ้างถึงองค์ประกอบสองอย่างในรายการนี้หรือไม่

ฉันเชื่อว่าคุณสามารถทำสิ่งนี้ได้ด้วยการเข้าร่วม var differResults = จาก c1 ใน myList เข้าร่วม c2 ใน myList บน


0

หากDistinct()ไม่ให้ผลลัพธ์ที่ไม่ซ้ำลองแบบนี้:

var filteredWC = tblWorkCenter.GroupBy(cc => cc.WCID_I).Select(grp => grp.First()).Select(cc => new Model.WorkCenter { WCID = cc.WCID_I }).OrderBy(cc => cc.WCID); 

ObservableCollection<Model.WorkCenter> WorkCenter = new ObservableCollection<Model.WorkCenter>(filteredWC);

0

แพคเกจ Microsoft System.Interactiveมีรุ่นที่แตกต่างที่ใช้แลมบ์ดาที่สำคัญผู้เลือก นี่เป็นวิธีเดียวกับวิธีแก้ปัญหาของ Jon Skeet แต่อาจเป็นประโยชน์สำหรับคนที่จะรู้และดูที่ห้องสมุดที่เหลือ


0

นี่คือวิธีที่คุณสามารถทำได้:

public static class Extensions
{
    public static IEnumerable<T> MyDistinct<T, V>(this IEnumerable<T> query,
                                                    Func<T, V> f, 
                                                    Func<IGrouping<V,T>,T> h=null)
    {
        if (h==null) h=(x => x.First());
        return query.GroupBy(f).Select(h);
    }
}

วิธีนี้ช่วยให้คุณสามารถใช้มันได้โดยการระบุพารามิเตอร์หนึ่งอย่าง.MyDistinct(d => d.Name)แต่ก็ยังช่วยให้คุณระบุเงื่อนไขที่มีเป็นพารามิเตอร์ตัวที่สองเช่น:

var myQuery = (from x in _myObject select x).MyDistinct(d => d.Name,
        x => x.FirstOrDefault(y=>y.Name.Contains("1") || y.Name.Contains("2"))
        );

NBสิ่งนี้จะช่วยให้คุณสามารถระบุฟังก์ชั่นอื่น ๆ เช่นเช่น.LastOrDefault(...)กัน


หากคุณต้องการเปิดเผยเพียงแค่เงื่อนไขคุณสามารถทำให้มันง่ายยิ่งขึ้นโดยใช้มันเป็น:

public static IEnumerable<T> MyDistinct2<T, V>(this IEnumerable<T> query,
                                                Func<T, V> f,
                                                Func<T,bool> h=null
                                                )
{
    if (h == null) h = (y => true);
    return query.GroupBy(f).Select(x=>x.FirstOrDefault(h));
}

ในกรณีนี้แบบสอบถามจะมีลักษณะดังนี้:

var myQuery2 = (from x in _myObject select x).MyDistinct2(d => d.Name,
                    y => y.Name.Contains("1") || y.Name.Contains("2")
                    );

หมายเหตุที่นี่การแสดงออกง่ายขึ้น แต่โน้ต.MyDistinct2ใช้.FirstOrDefault(...)โดยนัย


หมายเหตุ:ตัวอย่างข้างต้นกำลังใช้คลาสการสาธิตต่อไปนี้

class MyObject
{
    public string Name;
    public string Code;
}

private MyObject[] _myObject = {
    new MyObject() { Name = "Test1", Code = "T"},
    new MyObject() { Name = "Test2", Code = "Q"},
    new MyObject() { Name = "Test2", Code = "T"},
    new MyObject() { Name = "Test5", Code = "Q"}
};

0

IEnumerable แลมบ์ดาขยาย:

public static class ListExtensions
{        
    public static IEnumerable<T> Distinct<T>(this IEnumerable<T> list, Func<T, int> hashCode)
    {
        Dictionary<int, T> hashCodeDic = new Dictionary<int, T>();

        list.ToList().ForEach(t => 
            {   
                var key = hashCode(t);
                if (!hashCodeDic.ContainsKey(key))
                    hashCodeDic.Add(key, t);
            });

        return hashCodeDic.Select(kvp => kvp.Value);
    }
}

การใช้งาน:

class Employee
{
    public string Name { get; set; }
    public int EmployeeID { get; set; }
}

//Add 5 employees to List
List<Employee> lst = new List<Employee>();

Employee e = new Employee { Name = "Shantanu", EmployeeID = 123456 };
lst.Add(e);
lst.Add(e);

Employee e1 = new Employee { Name = "Adam Warren", EmployeeID = 823456 };
lst.Add(e1);
//Add a space in the Name
Employee e2 = new Employee { Name = "Adam  Warren", EmployeeID = 823456 };
lst.Add(e2);
//Name is different case
Employee e3 = new Employee { Name = "adam warren", EmployeeID = 823456 };
lst.Add(e3);            

//Distinct (without IEqalityComparer<T>) - Returns 4 employees
var lstDistinct1 = lst.Distinct();

//Lambda Extension - Return 2 employees
var lstDistinct = lst.Distinct(employee => employee.EmployeeID.GetHashCode() ^ employee.Name.ToUpper().Replace(" ", "").GetHashCode()); 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.