จะตรวจสอบได้อย่างไรว่ารายการทั้งหมดมีค่าเท่ากันและส่งคืนหรือคืนค่า "otherValue" หากไม่มี


122

หากรายการทั้งหมดในรายการมีค่าเท่ากันฉันจำเป็นต้องใช้ค่านั้นมิฉะนั้นฉันต้องใช้“ otherValue” ฉันไม่สามารถคิดวิธีที่ง่ายและชัดเจนในการทำเช่นนี้

ดูวิธีการเขียนลูปที่มีตรรกะพิเศษสำหรับรายการแรกในคอลเล็กชันด้วย


เมื่อคุณได้รับความสนใจที่ค่อนข้างหน้าด้านฉันจะไปกับคำตอบของ Ani stackoverflow.com/questions/4390232/…
Binary Worrier

5
คุณต้องการให้เกิดอะไรขึ้นหากไม่มีค่าแรกเนื่องจากรายการว่างเปล่า ในกรณีนี้มันเป็นความจริงที่ว่า "ของทั้งหมดในรายการมีค่าเท่ากัน" - ถ้าคุณไม่เชื่อฉันหาฉันที่ไม่มี! คุณไม่ได้กำหนดว่าจะทำอย่างไรในสถานการณ์นี้ สิ่งนี้ควรทำให้เกิดข้อยกเว้นส่งคืนค่า "อื่น ๆ " หรืออะไร
Eric Lippert

@Eric ขออภัยเมื่อรายการว่างเปล่าควรส่งคืนค่า "อื่น ๆ "
Ian Ringrose

คำตอบ:


153
var val = yyy.First().Value;
return yyy.All(x=>x.Value == val) ? val : otherValue; 

วิธีที่สะอาดที่สุดที่ฉันคิดได้ คุณสามารถทำให้เป็นซับเดียวได้โดย inlining val แต่ First () จะได้รับการประเมิน n ครั้งโดยเพิ่มเวลาดำเนินการเป็นสองเท่า

หากต้องการรวมลักษณะการทำงาน "ชุดว่าง" ที่ระบุไว้ในความคิดเห็นคุณเพียงเพิ่มอีกหนึ่งบรรทัดก่อนหน้าสองข้อด้านบน:

if(yyy == null || !yyy.Any()) return otherValue;

1
+1 จะใช้การ.Anyอนุญาตให้การแจงนับเลิกก่อนในกรณีที่มีค่าต่างกันหรือไม่
Jeff Ogata

12
@adrift: Allจะยุติโดยเร็วที่สุดเท่าที่มันจะกระทบองค์ประกอบของลำดับที่x x.Value != valในทำนองเดียวกันAny(x => x.Value != val)จะยุติโดยเร็วที่สุดเท่าที่มันจะกระทบองค์ประกอบของลำดับที่x x.Value != valนั่นคือทั้งสองAllและAnyจัดแสดง "การลัดวงจร" ที่คล้ายคลึงกับ&&และ||(ซึ่งเป็นสิ่งที่มีประสิทธิภาพAllและAnyเป็น)
สัน

@ เจสัน: เป๊ะ. ทั้งหมด (เงื่อนไข) เป็นไปอย่างมีประสิทธิภาพ! (! condition) ใด ๆ และการประเมินผลอย่างใดอย่างหนึ่งจะสิ้นสุดลงทันทีที่ทราบคำตอบ
KeithS

4
Microoptimisation:return yyy.Skip(1).All(x=>x.Value == val) ? val : otherValue;
Caltor

101

การทดสอบอย่างรวดเร็วที่ดีสำหรับทุกคนที่เท่าเทียมกัน:

collection.Distinct().Count() == 1

1
สิ่งนี้จะใช้ไม่ได้กับสิ่งใด ๆClassแม้ว่ามันควรจะทำงานกับโครงสร้าง เหมาะสำหรับรายการดั้งเดิม
Andrew Backer

2
+1 สะอาดกว่า IMO โซลูชันของ KeithS คุณอาจต้องการใช้ collection.Distinct().Count() <= 1 หากคุณต้องการอนุญาตคอลเลกชันที่ว่างเปล่า
3dGrabber

4
โปรดใช้ความระมัดระวัง.Distinct()ไม่เคยทำงานตามที่คาดไว้ - โดยเฉพาะอย่างยิ่งเมื่อคุณทำงานกับวัตถุเห็นนี้คำถาม ในกรณีนี้คุณต้องติดตั้งอินเทอร์เฟซ IEquatable
Matt

16
สะอาดกว่าใช่ แต่ประสิทธิภาพน้อยกว่าในกรณีทั่วไป มีการรับประกันความแตกต่าง () ว่าจะสำรวจทุกองค์ประกอบในคอลเลกชันหนึ่งครั้งและในกรณีที่เลวร้ายที่สุดของทุกองค์ประกอบที่แตกต่างกัน Count () จะสำรวจรายการทั้งหมดสองครั้ง Distinct () ยังสร้าง HashSet เพื่อให้พฤติกรรมของมันเป็นแบบเส้นตรงและไม่ใช่ NlogN หรือแย่กว่านั้นและจะทำให้การใช้งานหน่วยความจำสูงขึ้น All () ทำให้เต็มหนึ่งครั้งในกรณีที่แย่ที่สุดที่องค์ประกอบทั้งหมดเท่ากันและไม่สร้างคอลเล็กชันใหม่ใด ๆ
KeithS

1
@KeithS ตามที่ฉันหวังว่าคุณจะรู้ในตอนนี้Distinctจะไม่ข้ามคอลเลกชันเลยและCountจะทำการสำรวจผ่านทางDistinctiterator ของ
NetMage

22

แม้ว่าคุณจะสามารถสร้างอุปกรณ์ดังกล่าวจากตัวดำเนินการลำดับที่มีอยู่ได้อย่างแน่นอน แต่ในกรณีนี้ฉันก็อยากจะเขียนสิ่งนี้เป็นตัวดำเนินการลำดับที่กำหนดเอง สิ่งที่ต้องการ:

// Returns "other" if the list is empty.
// Returns "other" if the list is non-empty and there are two different elements.
// Returns the element of the list if it is non-empty and all elements are the same.
public static int Unanimous(this IEnumerable<int> sequence, int other)
{
    int? first = null;
    foreach(var item in sequence)
    {
        if (first == null)
            first = item;
        else if (first.Value != item)
            return other;
    }
    return first ?? other;
}

ค่อนข้างชัดเจนสั้นครอบคลุมทุกกรณีและไม่สร้างการวนซ้ำเพิ่มเติมของลำดับโดยไม่จำเป็น

ทำให้เป็นวิธีการทั่วไปที่ใช้ได้ผลIEnumerable<T>คือการออกกำลังกาย :-)


ตัวอย่างเช่นคุณมีลำดับของ nullables และค่าที่แยกออกมาก็เป็นค่าว่างได้เช่นกัน ในกรณีนี้ลำดับอาจว่างเปล่าหรือทุกรายการในลำดับอาจมีค่าว่างในค่าที่แยกออกมา ในกรณีนี้การรวมตัวกันจะส่งคืนotherเมื่อเป็นการnullตอบสนองที่ถูกต้อง (น่าจะเป็น) สมมติว่าฟังก์ชั่นเป็นT Unanimous<U, T>(this IEnumerable<U> sequence, T other)หรือลายเซ็นบางอย่างที่ทำให้ซับซ้อนขึ้นเล็กน้อย
Anthony Pegram

@ แอนโธนี: อันที่จริงมีภาวะแทรกซ้อนมากมายที่นี่ แต่ก็สามารถแก้ไขได้ง่าย ฉันใช้ int nullable เพื่อความสะดวกเพื่อที่ฉันจะได้ไม่ต้องประกาศแฟล็ก "ฉันเห็นรายการแรกแล้ว" คุณสามารถประกาศธงได้อย่างง่ายดาย นอกจากนี้ฉันใช้ "int" แทน T เพราะฉันรู้ว่าคุณสามารถเปรียบเทียบสอง ints เพื่อความเท่าเทียมกันได้เสมอซึ่งไม่ใช่กรณีของสอง Ts นี่เป็นภาพร่างของโซลูชันมากกว่าโซลูชันทั่วไปที่ทำงานได้อย่างสมบูรณ์
Eric Lippert

13
return collection.All(i => i == collection.First())) 
    ? collection.First() : otherValue;.

หรือหากคุณกังวลเกี่ยวกับการเรียกใช้ First () สำหรับแต่ละองค์ประกอบ (ซึ่งอาจเป็นปัญหาด้านประสิทธิภาพที่ถูกต้อง):

var first = collection.First();
return collection.All(i => i == first) ? first : otherValue;

@KeithS - นี่คือเหตุผลที่ฉันเพิ่มส่วนที่สองของคำตอบของฉัน ในคอลเลกชันขนาดเล็กการเรียก First () เป็นเรื่องเล็กน้อย ในคอลเลกชันขนาดใหญ่นั่นอาจเป็นปัญหา
Justin Niessner

1
"ในคอลเล็กชันขนาดเล็กการเรียก First () เป็นเรื่องเล็กน้อย" - ขึ้นอยู่กับแหล่งที่มาของคอลเลกชัน สำหรับรายการหรืออาร์เรย์ของออบเจ็กต์ธรรมดาคุณคิดถูกแล้ว แต่การแจงนับบางตัวไม่ใช่ชุดดั้งเดิมที่แคชหน่วยความจำ จำกัด ชุดของผู้รับมอบสิทธิ์หรือตัวแจงนับที่ให้ผลตอบแทนจากการคำนวณอนุกรมอัลกอริทึม (เช่น Fibonacci) จะมีราคาแพงมากในการประเมิน First () ทุกครั้ง
KeithS

5
หรือแย่กว่านั้นถ้าแบบสอบถามเป็นแบบสอบถามฐานข้อมูลและการเรียก "First" จะเข้าสู่ฐานข้อมูลอีกครั้งทุกครั้ง
Eric Lippert

1
มันแย่ลงเมื่อคุณทำซ้ำเพียงครั้งเดียวเช่นอ่านจากไฟล์ ... ดังนั้นคำตอบของ Ani จากหัวข้ออื่นจึงดูดีที่สุด
Alexei Levenkov

@ เอริก - C'mon. ไม่มีอะไรผิดพลาดในการกดฐานข้อมูลสามครั้งสำหรับแต่ละองค์ประกอบ ... :-P
Justin Niessner

3

อาจจะช้าไป แต่ส่วนขยายที่ใช้ได้กับค่าและประเภทอ้างอิงตามคำตอบของ Eric:

public static partial class Extensions
{
    public static Nullable<T> Unanimous<T>(this IEnumerable<Nullable<T>> sequence, Nullable<T> other, IEqualityComparer comparer = null)  where T : struct, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (Nullable<T>)first ?? other;
    }

    public static T Unanimous<T>(this IEnumerable<T> sequence, T other, IEqualityComparer comparer = null)  where T : class, IComparable
    {
        object first = null;
        foreach(var item in sequence)
        {
            if (first == null)
                first = item;
            else if (comparer != null && !comparer.Equals(first, item))
                return other;
            else if (!first.Equals(item))
                return other;
        }
        return (T)first ?? other;
    }
}


1

ทางเลือกในการใช้ LINQ:

var set = new HashSet<int>(values);
return (1 == set.Count) ? values.First() : otherValue;

ฉันพบว่าการใช้HashSet<T>นั้นเร็วกว่าสำหรับรายการจำนวนเต็มมากถึง ~ 6,000 รายการเมื่อเทียบกับ:

var value1 = items.First();
return values.All(v => v == value1) ? value1: otherValue;

ประการแรกอาจสร้างขยะจำนวนมาก นอกจากนี้ยังมีความชัดเจนน้อยกว่าคำตอบของ LINQ อื่น ๆ แต่ช้ากว่าวิธีการขยายคำตอบ
Ian Ringrose

จริง อย่างไรก็ตามจะไม่เป็นขยะมากนักหากเรากำลังพูดถึงการพิจารณาว่าชุดค่าเล็ก ๆ นั้นเหมือนกันทั้งหมดหรือไม่ เมื่อฉันเรียกใช้สิ่งนี้และคำสั่ง LINQ ใน LINQPad สำหรับชุดค่าเล็ก ๆ HashSet นั้นเร็วกว่า (จับเวลาโดยใช้คลาส Stopwatch)
ƉiamondǤeezeƦ

หากคุณเรียกใช้ในเวอร์ชันสร้างจากบรรทัดคำสั่งคุณอาจได้รับผลลัพธ์ที่แตกต่างกัน
Ian Ringrose

สร้างแอปพลิเคชันคอนโซลและพบว่าHashSet<T>ในตอนแรกเร็วกว่าการใช้คำสั่ง LINQ ในคำตอบของฉัน อย่างไรก็ตามหากฉันทำสิ่งนี้แบบวนซ้ำ LINQ จะเร็วกว่า
ƉiamondǤeezeƦ

ใหญ่ไม่เห็นด้วยกับการแก้ปัญหานี้ก็คือว่าถ้าคุณใช้คลาสที่กำหนดเองของคุณคุณต้องใช้ของคุณเองGetHashCode()ซึ่งเป็นเรื่องยากที่จะทำอย่างถูกต้องดู: stackoverflow.com/a/371348/2607840สำหรับรายละเอียดเพิ่มเติม
Cameron

0

การเปลี่ยนแปลงเล็กน้อยในแนวทางที่เรียบง่ายข้างต้น

var result = yyy.Distinct().Count() == yyy.Count();


3
นี่คืออีกทางหนึ่ง สิ่งนี้จะตรวจสอบว่าทุกองค์ประกอบในรายการไม่ซ้ำกัน
Mario Galea

-1

หากอาร์เรย์เป็นประเภทหลายมิติเช่นด้านล่างเราจะต้องเขียน linq ด้านล่างเพื่อตรวจสอบข้อมูล

ตัวอย่าง: องค์ประกอบที่นี่คือ 0 และฉันกำลังตรวจสอบค่าทั้งหมดว่าเป็น 0 หรือไม่
ip1 =
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0 0 0

    var value=ip1[0][0];  //got the first index value
    var equalValue = ip1.Any(x=>x.Any(xy=>xy.Equals()));  //check with all elements value 
    if(equalValue)//returns true or false  
    {  
    return "Same Numbers";  
    }else{  
    return "Different Numbers";   
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.