如何对 IEnumerable<string> 进行排序

Posted

技术标签:

【中文标题】如何对 IEnumerable<string> 进行排序【英文标题】:How to sort an IEnumerable<string> 【发布时间】:2011-04-07 12:55:12 【问题描述】:

如何按字母顺序对IEnumerable&lt;string&gt; 进行排序。这可能吗?

编辑:我将如何编写就地解决方案?

【问题讨论】:

【参考方案1】:

与您对任何其他可枚举的排序方式相同:

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,在枚举时,它会按排序顺序返回原始 IEnumerable 的元素。它不会就地对 IEnumerable 进行排序。


IEnumerable 是只读的,即只能从中检索元素,而不能直接修改。如果要对字符串集合进行就地排序,则需要对实现了 IEnumerable 的原始集合进行排序,或者先将 IEnumerable 变成可排序的集合:

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)。 所以,_components = _components.OrderBy(s => s);会好吗? @CatZilla:应该可以,但可能不是解决实际问题的最佳方法。 _components是什么,从哪里获取,如何使用? @dtb:哦,_components 正是通过这种方式从 XML 文件中填充的:_components = (from c in xml.Descendants("component") select c.Value.ToString()).Distinct( ).ToList();我需要对其进行排序。 OrderBy 返回IOrderedEnumerable&lt;T&gt;IOrderedEnumerable&lt;T&gt; 派生自 IEnumerable&lt;T&gt;,因此它可以像 IEnumerable&lt;T&gt; 一样使用,但它扩展了类型,例如,允许使用 ThenBy【参考方案2】:

这是不可能的,但事实并非如此。

基本上,任何排序方法都会将您的IEnumerable 复制到List,对List 进行排序,然后将排序后的列表返回给您,即IEnumerableIList

这意味着您失去了 IEnumerable 的“无限继续”属性,但无论如何您都无法对它进行排序。

【讨论】:

正确。 IEnumerable 的目的是为您提供一个系列的句柄,您可以通过继续询问“下一个”项目来从头到尾进行迭代。这意味着一个 IEnumerable 可以在所有内容已知之前进行部分迭代;你不必知道什么时候你已经完成了所有这些,直到你完成了。排序(就像 Linq 让你做的许多事情一样)需要将整个系列作为有序列表的知识;排序后的系列中最先出现的项目可能是系列返回的最后一个,除非您知道所有项目是什么,否则您不会知道这一点。【参考方案3】:
myEnumerable = myEnumerable.OrderBy(s => s);

【讨论】:

【参考方案4】:

我们不能总是在原地进行,但我们会检测何时可能:

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 但不知道实现的实际类型,您可能不应该修改它。顺便说一句,Array.FunctorComparer 是内部的。 在修改我们所拥有的内容时,我认为这暗示在寻找就地的问题中;它暗示了它。在方法名称中包含“InPlaceInCan”是一个原因;方法名称比最好的文档更能提前说明风险;)是的,Array.FunctorComparer 是内部的,但它是微不足道的。我把它放进去是因为这是我能想到的最好的方式,例如“你的函子比较器在你的首选助手类中”。 @dtb 重新考虑,改为使用我自己的(再次查看 Array.FunctorComparer,无论如何我更喜欢我的!) 聪明的想法和命名!但是为什么listToSort = (src is List&lt;T&gt;) ? (List&lt;T&gt;)src : new List&lt;T&gt;(src); 中的double casting anti pattern 呢?有没有像listToSort = (src as List&lt;T&gt;); if (null == listToSort) listToSort = new List&lt;T&gt;(src); @JeroenWiertPluimers 示例代码也总是存在问题;也许我只是没有打扰,因为上面的内容略短,我想专注于手头的事情,但即使在示例中也能阻止坏习惯。

以上是关于如何对 IEnumerable<string> 进行排序的主要内容,如果未能解决你的问题,请参考以下文章

如何获取枚举值属性的 IEnumerable<string>?

如何在 C# 中使用 Ienumerable 反转一系列元素?

在List之间自由转换 和IEnumerable

将 IEnumerable<int> 中的所有值相乘

NET问答: 为什么 IEnumerable<string; 不能被初始化?

IEnumerable<T> 的 String.Join(string, string[]) 的模拟