如何对IEnumerable <string>进行排序


Answers:


154

对其他所有枚举进行排序的方式相同:

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>是只读的,也就是说,您只能从中检索元素,而不能直接对其进行修改。如果要就地对字符串集合进行排序,则需要对实现IEnumerable <string>的原始集合进行排序,或者首先将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 2010年

因此,_components = _components.OrderBy(s => s); 会好吗?
CatZilla

@CatZilla:可以,但是可能不是解决实际问题的最佳方法。什么是_components,您从哪里获得它,如何使用它?
dtb

1
@dtb:哦,正是通过这种方式从XML文件填充_components:_components =(从xml.Descendants(“ component”)中的c中选择c.Value.ToString())。Distinct()。ToList(); 我需要对其进行排序。
CatZilla

2
OrderBy返回IOrderedEnumerable<T>IOrderedEnumerable<T>派生自,IEnumerable<T>因此它可以像一样使用IEnumerable<T>,但它扩展了类型,例如允许使用ThenBy
Maciej Hehl

12

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

基本上,任何排序方法都会将您复制IEnumerable到中List,对进行排序List,然后再将排序后的列表返回给您,该列表既是IEnumerable也是IList

这意味着您失去了的“无限连续”属性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>,但不知道实现的实际类型,则可能不应该对其进行修改。顺便说一句,Array.FunctorComparer <T>是内部的。
dtb

在修改我们拥有的内容时,我认为这是在就地寻找问题中所隐含的;暗示它。这是在方法名称中使用“ InPlaceInCan”的原因。方法名称可能比最佳文档更能说明风险;)是的,Array.FunctorComparer <T>是内部的,但这很琐碎。我之所以这样说是因为,这是我在“您的辅助类中拥有的函子比较器”示例中想到的最佳方式。
乔恩·汉娜

@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 2013年

1
@JeroenWiertPluimers示例代码也总是存在问题;也许我只是不打扰,因为上述内容略短了一些,我想专心处理这件事,但是即使在例子中,也可以阻止不良习惯。
乔恩·汉纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.