ความคิดเกี่ยวกับวิธีการตรวจสอบว่ารายการนั้นเป็นส่วนย่อยของอีก
โดยเฉพาะฉันมี
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
วิธีตรวจสอบว่า t2 เป็นชุดย่อยของ t1 โดยใช้ LINQ อย่างไร
ความคิดเกี่ยวกับวิธีการตรวจสอบว่ารายการนั้นเป็นส่วนย่อยของอีก
โดยเฉพาะฉันมี
List<double> t1 = new List<double> { 1, 3, 5 };
List<double> t2 = new List<double> { 1, 5 };
วิธีตรวจสอบว่า t2 เป็นชุดย่อยของ t1 โดยใช้ LINQ อย่างไร
คำตอบ:
bool isSubset = !t2.Except(t1).Any();
ใช้ HashSet แทน List ถ้าทำงานกับชุด จากนั้นคุณสามารถใช้IsSubsetOf ()
HashSet<double> t1 = new HashSet<double>{1,3,5};
HashSet<double> t2 = new HashSet<double>{1,5};
bool isSubset = t2.IsSubsetOf(t1);
ขออภัยที่ไม่ใช้ LINQ :-(
หากคุณต้องการใช้รายการวิธีแก้ปัญหาของ @ Jared จะทำงานร่วมกับข้อแม้ที่คุณจะต้องลบองค์ประกอบที่มีอยู่ซ้ำ ๆ
หากคุณทำการทดสอบหน่วยคุณสามารถใช้เมธอดCollectionAssert.IsSubsetOf :
CollectionAssert.IsSubsetOf(subset, superset);
ในกรณีข้างต้นสิ่งนี้จะหมายถึง:
CollectionAssert.IsSubsetOf(t2, t1);
นี่เป็นโซลูชันที่มีประสิทธิภาพมากกว่าที่อื่น ๆ ที่โพสต์ไว้ที่นี่โดยเฉพาะโซลูชันยอดนิยม:
bool isSubset = t2.All(elem => t1.Contains(elem));
หากคุณสามารถหาองค์ประกอบเดียวใน t2 ที่ไม่ได้อยู่ใน t1 คุณจะรู้ว่า t2 ไม่ใช่ชุดย่อยของ t1 ข้อดีของวิธีนี้คือทำทุกอย่างในสถานที่โดยไม่ต้องจัดสรรพื้นที่เพิ่มเติมซึ่งแตกต่างจากโซลูชันที่ใช้. ยกเว้นหรือ. Intersect นอกจากนี้วิธีนี้จะสามารถแตกได้ทันทีที่พบองค์ประกอบหนึ่งที่ละเมิดเงื่อนไขส่วนย่อยในขณะที่คนอื่น ๆ ยังคงค้นหา ด้านล่างเป็นรูปแบบที่ยาวที่สุดของการแก้ปัญหาซึ่งเร็วกว่าเล็กน้อยในการทดสอบของฉันมากกว่าวิธีการจดชวเลขข้างต้น
bool isSubset = true;
foreach (var element in t2) {
if (!t1.Contains(element)) {
isSubset = false;
break;
}
}
ฉันทำการวิเคราะห์ประสิทธิภาพพื้นฐานของโซลูชันทั้งหมดและผลลัพธ์นั้นรุนแรงมาก โซลูชันทั้งสองนี้เร็วกว่าโซลูชัน. ยกเว้น () และ .Intersect () ประมาณ 100x และไม่ต้องใช้หน่วยความจำเพิ่มเติม
!t2.Except(t1).Any()
กำลังทำอยู่ Linq กำลังทำงานกลับไปกลับมา Any()
กำลังถามIEnumerable
ว่ามีองค์ประกอบอย่างน้อยหนึ่งองค์ประกอบหรือไม่ ในสถานการณ์t2.Except(t1)
นี้ปล่อยองค์ประกอบแรกt2
ที่ไม่ได้อยู่ในองค์ประกอบt1
เท่านั้น หากองค์ประกอบแรกของt2
ไม่อยู่ในt1
นั้นจะเสร็จเร็วที่สุดหากองค์ประกอบทั้งหมดt2
อยู่ในt1
นั้นจะทำงานได้นานที่สุด
t1={1,2,3,...9999}
และคุณจะได้รับการตรวจวัดดังต่อไปนี้:t2={9999,9998,99997...9000}
!t2.Except(t1).Any(): 1ms -> t2.All(e => t1.Contains(e)): 702ms
และยิ่งแย่ลงยิ่งช่วงกว้างยิ่งขึ้น
t2.Except (t1)
ส่งคืนไม่ได้เป็นIEnumerable
Collection
มันจะปล่อยไอเท็มที่เป็นไปได้ทั้งหมดหากคุณวนซ้ำอย่างสมบูรณ์เช่นToArray ()
หรือโดยการToList ()
ใช้หรือforeach
ไม่ใช้ภายใน ค้นหาlinq deferred executionเพื่ออ่านเพิ่มเติมเกี่ยวกับแนวคิดนั้น
t2={1,2,3,4,5,6,7,8}
t1={2,4,6,8}
t2.Except(t1)
=> องค์ประกอบแรกของ t2 = 1 => ความแตกต่างของ1ถึง t1 คือ1 (ตรวจสอบกับ {2,4,6,8}) => Except()
ปล่อยองค์ประกอบแรก1 => Any()
รับองค์ประกอบ => Any()
ผลลัพธ์ใน true => ไม่มีการตรวจสอบองค์ประกอบเพิ่มเติมใน t2
วิธีการแก้ปัญหา @ Cameron เป็นวิธีการขยาย:
public static bool IsSubsetOf<T>(this IEnumerable<T> a, IEnumerable<T> b)
{
return !a.Except(b).Any();
}
การใช้งาน:
bool isSubset = t2.IsSubsetOf(t1);
(นี่คล้ายกัน แต่ไม่เหมือนกับที่โพสต์บนบล็อกของ @ Michael)
การสร้างคำตอบจาก @Cameron และ @Neil ฉันได้เขียนวิธีการขยายที่ใช้คำศัพท์เดียวกันกับคลาส Enumerable
/// <summary>
/// Determines whether a sequence contains the specified elements by using the default equality comparer.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <param name="source">A sequence in which to locate the values.</param>
/// <param name="values">The values to locate in the sequence.</param>
/// <returns>true if the source sequence contains elements that have the specified values; otherwise, false.</returns>
public static bool ContainsAll<TSource>(this IEnumerable<TSource> source, IEnumerable<TSource> values)
{
return !values.Except(source).Any();
}
ที่นี่เราตรวจสอบว่าหากมีองค์ประกอบใด ๆ ที่อยู่ในรายการลูก (เช่น
t2
) ซึ่งไม่ได้อยู่ในรายชื่อผู้ปกครอง (เช่นt1
) หากไม่มีใครอยู่แล้วรายการนั้นเป็นส่วนย่อยของอีก
เช่น:
bool isSubset = !(t2.Any(x => !t1.Contains(x)));
ลองสิ่งนี้
static bool IsSubSet<A>(A[] set, A[] toCheck) {
return set.Length == (toCheck.Intersect(set)).Count();
}
แนวคิดที่นี่คือ Intersect จะคืนค่าที่อยู่ในทั้งสองอาร์เรย์เท่านั้น ณ จุดนี้หากความยาวของชุดผลลัพธ์เป็นเหมือนชุดเดิมองค์ประกอบทั้งหมดใน "set" จะอยู่ใน "check" ด้วยดังนั้น "set" จึงเป็นชุดย่อยของ "toCheck"
หมายเหตุ: โซลูชันของฉันไม่ทำงานหาก "set" ซ้ำกัน ฉันไม่เปลี่ยนเพราะฉันไม่ต้องการขโมยคะแนนของคนอื่น
คำแนะนำ: ฉันโหวตให้คำตอบของ Cameron