วิธีการเรียงลำดับ IEnumerable <string>


100

ฉันจะเรียงลำดับIEnumerable<string>ตามตัวอักษรได้อย่างไร เป็นไปได้หรือไม่

แก้ไข: ฉันจะเขียนโซลูชันแบบแทนที่ได้อย่างไร

คำตอบ:


158

วิธีเดียวกับที่คุณจัดเรียงอื่น ๆ ที่ระบุได้:

var result = myEnumerable.OrderBy(s => s);

หรือ

var result = from s in myEnumerable
             orderby s
             select s;

หรือ (ละเว้นกรณี)

var result = myEnumerable.OrderBy(s => s,
                                  StringComparer.CurrentCultureIgnoreCase);

โปรดทราบว่าตามปกติของ LINQ สิ่งนี้จะสร้าง IEnumerable <T> ใหม่ซึ่งเมื่อแจกแจงแล้วจะส่งคืนองค์ประกอบของ IEnumerable <T> ดั้งเดิมตามลำดับที่เรียง ไม่เรียงลำดับ IEnumerable <T> ในสถานที่


IEnumerable <T> เป็นแบบอ่านอย่างเดียวนั่นคือคุณสามารถดึงองค์ประกอบจากมันได้ แต่ไม่สามารถแก้ไขได้โดยตรง หากคุณต้องการจัดเรียงคอลเลกชันของสตริงในตำแหน่งคุณต้องเรียงลำดับคอลเลคชันดั้งเดิมที่ใช้ <string> ของ IEnumerable หรือเปลี่ยน IEnumerable <string> เป็นคอลเลกชันที่เรียงลำดับได้ก่อน:

List<string> myList = myEnumerable.ToList();
myList.Sort();

ตามความคิดเห็นของคุณ:

_components = (from c in xml.Descendants("component")
               let value = (string)c
               orderby value
               select value
              )
              .Distinct()
              .ToList();

หรือ

_components = xml.Descendants("component")
                 .Select(c => (string)c)
                 .Distinct()
                 .OrderBy(v => v)
                 .ToList();

หรือ (หากคุณต้องการเพิ่มรายการเพิ่มเติมในรายการในภายหลังและจัดเรียงไว้)

_components = xml.Descendants("component")
                 .Select(c => (string)c)
                 .Distinct()
                 .ToList();

_components.Add("foo");
_components.Sort();

หรือ myEnumerable.OrderByDescending (s => s)
Grozz

ดังนั้น _components = _components.OrderBy (s => s); จะดีไหม?
CatZilla

@CatZilla: นั่นน่าจะใช้ได้ แต่อาจไม่ใช่วิธีที่ดีที่สุดในการแก้ปัญหาที่แท้จริงของคุณ _components คืออะไรคุณเอามาจากไหนใช้ยังไง?
dtb

1
@dtb: โอ้ _components ถูกสร้างจากไฟล์ XML ด้วยวิธีนี้อย่างแม่นยำ: _components = (จาก c ใน xml.Descendants ("component") เลือก c.Value ToString ()) Distinct (). ToList (); และฉันต้องเรียงลำดับ
CatZilla

2
OrderByIOrderedEnumerable<T>ผลตอบแทน IOrderedEnumerable<T>มาจากIEnumerable<T>เพื่อที่จะสามารถนำมาใช้เช่นแต่จะขยายประเภทที่ช่วยให้เช่นสำหรับการใช้งานของIEnumerable<T> ThenBy
Maciej Hehl

12

มันเป็นไปไม่ได้ แต่มันไม่ใช่

โดยทั่วไปวิธีการเรียงลำดับใด ๆ ที่เป็นไปเพื่อคัดลอกของคุณIEnumerableเป็นList, การจัดเรียงListแล้วกลับไปที่คุณรายการที่เรียงลำดับซึ่งเป็นเช่นเดียวกับIEnumerableIList

ซึ่งหมายความว่าคุณสูญเสียคุณสมบัติ "ดำเนินต่อไปไม่สิ้นสุด" ของ an IEnumerableแต่คุณก็ไม่สามารถเรียงลำดับได้เช่นนั้น


7
ขวา จุดประสงค์ของ IEnumerable คือการนำเสนอซีรีส์ที่คุณสามารถทำซ้ำตั้งแต่ต้นจนจบโดยถามรายการ "ถัดไป" ต่อไป ซึ่งหมายความว่า IEnumerable สามารถทำซ้ำได้บางส่วนก่อนที่เนื้อหาทั้งหมดจะทราบ คุณไม่จำเป็นต้องรู้ว่าเมื่อไหร่ที่คุณผ่านมันไปทั้งหมดจนกว่าคุณจะมี การเรียงลำดับ (เช่นเดียวกับหลาย ๆ สิ่งที่ Linq ให้คุณทำได้) ต้องใช้ความรู้เกี่ยวกับซีรีส์ทั้งหมดเป็นรายการสั่ง รายการที่จะปรากฏเป็นอันดับแรกในซีรีส์ที่เรียงลำดับอาจเป็นรายการสุดท้ายที่ส่งคืนโดยซีรีส์และคุณจะไม่รู้เลยเว้นแต่คุณจะรู้ว่ารายการทั้งหมดคืออะไร
KeithS


2

เราไม่สามารถทำได้ในสถานที่เสมอไป แต่เราตรวจพบเมื่อเป็นไปได้:

IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src, IComparer<T> cmp)
{
  List<T> listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);
  listToSort.Sort(cmp);
  return listToSort;
}
IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src, Comparison<T> cmp)
{
  return SortInPlaceIfCan(src, new FuncComparer<T>(cmp));
}
IEnumerable<T> SortInPlaceIfCan(IEnumerable<T> src)
{
  return SortInPlaceIfCan(src, Comparer<T>.Default);
}

สิ่งนี้ใช้โครงสร้างที่มีประโยชน์ต่อไปนี้:

internal struct FuncComparer<T> : IComparer<T>
{
  private readonly Comparison<T> _cmp;
  public FuncComparer(Comparison<T> cmp)
  {
      _cmp = cmp;
  }
  public int Compare(T x, T y)
  {
      return _cmp(x, y);
  }
}

ฉันไม่แน่ใจว่าจะแนะนำสิ่งนี้หรือไม่ หากคุณมี IEnumerable <T> แต่ไม่ทราบประเภทจริงที่ใช้งานคุณอาจไม่ควรแก้ไข Btw, Array.FunctorComparer <T> อยู่ภายใน
dtb

ในการปรับเปลี่ยนสิ่งที่เรามีฉันได้นำสิ่งนั้นมาเป็นนัยในคำถามที่กำลังมองหาในสถานที่ มันบอกเป็นนัยว่า เป็นเหตุผลที่ต้องมี "InPlaceInCan" ในชื่อวิธีการ ชื่อเมธอดสามารถแสดงความเสี่ยงได้มากกว่าเอกสารที่ดีที่สุด) ใช่ Array.FunctorComparer <T> เป็นแบบภายใน แต่มันไม่สำคัญ ฉันใส่ไว้เพราะเป็นวิธีที่ดีที่สุดที่ฉันคิดได้ในตัวอย่าง "functor Comparer ที่คุณมีในชุด go-to class helper class"
จอนฮันนา

@dtb ในความคิดที่สองเปลี่ยนมาใช้ของฉันเอง (ลองดู Array.FunctorComparer อีกครั้งและฉันชอบของฉันมากกว่า!)
จอนฮันนา

ไอเดียและการตั้งชื่อที่ชาญฉลาด! แต่ทำไมรูปแบบการต่อต้านการหล่อคู่ในlistToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? แล้วจะเป็นยังไงlistToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers

1
@JeroenWiertPluimers มักจะมีปัญหากับโค้ดตัวอย่างเช่นกัน บางทีฉันอาจจะไม่รำคาญเพราะข้างต้นสั้นกว่าเล็กน้อยและฉันต้องการมีสมาธิกับเรื่องที่อยู่ในมือ แต่เป็นการดีที่จะกีดกันนิสัยที่ไม่ดีแม้ในตัวอย่าง
Jon Hanna
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.