Cara mengurutkan <string> IEnumerable

98

Bagaimana saya bisa mengurutkan IEnumerable<string>menurut abjad. Apakah ini mungkin?

Sunting: Bagaimana saya menulis solusi di tempat?

CatZilla
sumber

Jawaban:

155

Dengan cara yang sama Anda mengurutkan yang lain yang dapat dihitung:

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

atau

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

atau (mengabaikan kasus)

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

Perhatikan bahwa, seperti biasa dengan LINQ, ini membuat IEnumerable <T> baru yang, ketika disebutkan, mengembalikan elemen IEnumerable <T> asli dalam urutan yang diurutkan. Itu tidak mengurutkan IEnumerable <T> di tempat.


IEnumerable <T> bersifat read-only, artinya, Anda hanya dapat mengambil elemen darinya, tetapi tidak dapat memodifikasinya secara langsung. Jika Anda ingin mengurutkan kumpulan string di tempat, Anda perlu mengurutkan koleksi asli yang mengimplementasikan IEnumerable <string>, atau mengubah IEnumerable <string> menjadi koleksi yang dapat diurutkan terlebih dahulu:

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

Berdasarkan komentar Anda:

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

atau

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

atau (jika nanti Anda ingin menambahkan lebih banyak item ke daftar dan tetap mengurutkannya)

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

_components.Add("foo");
_components.Sort();
dtb
sumber
atau myEnumerable.OrderByDescending (s => s).
Grozz
Jadi, _components = _components.OrderBy (s => s); akan baik-baik saja?
CatZilla
@CatZilla: Itu seharusnya berhasil, tetapi mungkin bukan cara terbaik untuk menyelesaikan masalah Anda yang sebenarnya. Apa itu _components, dari mana Anda mendapatkannya, bagaimana cara menggunakannya?
dtb
1
@dtb: Oh, _components diisi dari file XML persis seperti ini: _components = (dari c di xml.Descendants ("component") pilih c.Value.ToString ()). Distinct (). ToList (); Dan saya perlu menyortirnya.
CatZilla
2
OrderBykembali IOrderedEnumerable<T>. IOrderedEnumerable<T>berasal dari IEnumerable<T>sehingga dapat digunakan seperti IEnumerable<T>, tetapi memperluas jenis, memungkinkan misalnya, untuk penggunaan ThenBy.
Maciej Hehl
12

Itu tidak mungkin, tetapi sebenarnya tidak.

Pada dasarnya, metode pengurutan apa pun akan menyalin Anda IEnumerableke dalam List, mengurutkan, Listdan kemudian mengembalikan kepada Anda daftar yang diurutkan, yang IEnumerablejuga merupakan IList.

Ini berarti Anda kehilangan properti "lanjutkan tanpa batas" dari sebuah IEnumerable, tetapi Anda tidak dapat mengurutkan yang seperti itu.

James Curran
sumber
7
Benar. Tujuan IEnumerable adalah memberi Anda pegangan ke rangkaian yang dapat Anda ulangi, mulai dari akhir, dengan terus meminta item "berikutnya". Ini berarti bahwa IEnumerable dapat diulangi sebagian sebelum semua konten diketahui; Anda tidak perlu tahu kapan Anda telah melalui semuanya sampai Anda melakukannya. Penyortiran (seperti banyak hal yang memungkinkan Anda dilakukan Linq) membutuhkan pengetahuan tentang keseluruhan rangkaian sebagai daftar yang diurutkan; item yang akan muncul pertama dalam rangkaian yang diurutkan mungkin yang terakhir dikembalikan oleh rangkaian, dan Anda tidak akan mengetahuinya kecuali jika Anda mengetahui semua item itu.
KeithS
8
myEnumerable = myEnumerable.OrderBy(s => s);
Larsenal
sumber
2

Kami tidak selalu dapat melakukannya di tempat, tetapi kami mendeteksi jika memungkinkan:

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);
}

Ini menggunakan struct praktis berikut:

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);
  }
}
Jon Hanna
sumber
Saya tidak yakin apakah saya akan merekomendasikan ini. Jika Anda memiliki IEnumerable <T> tetapi tidak mengetahui jenis aktual yang diimplementasikan, Anda mungkin tidak boleh memodifikasinya. Btw, Array.FunctorComparer <T> bersifat internal.
dtb
Saat memodifikasi apa yang kami miliki, saya menganggapnya tersirat dalam pertanyaan mencari di tempat; yang menyiratkannya. Itu alasan untuk memiliki "InPlaceInCan" di nama metode; nama metode bisa lebih terbuka tentang risiko daripada dokumentasi terbaik;) Ya, Array.FunctorComparer <T> bersifat internal, tapi sepele. Saya memasukkannya ke dalamnya karena itu adalah cara terbaik yang dapat saya pikirkan dalam contoh untuk "pembanding fungsi Anda yang Anda miliki di kumpulan kelas pembantu".
Jon Hanna
@dtb pada pikiran kedua, diubah untuk menggunakan milik saya sendiri (lihat Array.FunctorComparer lagi dan saya lebih memilih milik saya!)
Jon Hanna
Ide dan penamaan cerdas! Tapi kenapa pola anti pengecoran ganda masuk listToSort = (src is List<T>) ? (List<T>)src : new List<T>(src);? Bagaimana kalau memilikinyalistToSort = (src as List<T>); if (null == listToSort) listToSort = new List<T>(src);
Jeroen Wiert Pluimers
1
@JeroenWiertPluimers selalu ada masalah dengan kode contoh juga; mungkin saya hanya tidak ambil pusing karena penjelasan di atas sedikit lebih pendek dan saya ingin berkonsentrasi pada masalah yang ada, tetapi itu baik untuk mencegah kebiasaan buruk bahkan dalam contoh.
Jon Hanna