1

在我的代码中,我使用collection<T>不同控件(WPF/C#)的绑定源。每次加载应用程序时都会从系统创建集合。我无法控制集合,并且每次启动应用程序时它的顺序(集合中的项目)都是随机的。

出于 UI 原因,我需要允许对集合进行排序,将其显示在列表视图中,并在用户修改时保持排序(上移和下移按钮)。因此我的想法很简单。我只是将逗号分隔的项目写入隐藏的字符串变量,例如。"itemX,ItemY,ItemZ".

现在我需要一个根据字符串对集合进行排序的函数。我在考虑一些 foreach 循环,但我确信有更好的方法来对集合进行排序。

  • collection<t>以与字符串表示的相同顺序对其中的项目进行排序。

    string correctItemOrder = "Application5, Application2, Application4, Application3". 
    

Collection<T>具有仅具有属性名称(例如"Applicaton3")但随机排序的项目。我想以与字符串相同的顺序对集合进行排序。

T是一个接口,我可以访问"Name"具有存储在字符串中的值的属性,例如。“项目 X”。

任何很酷的片段/功能?

谢谢

4

3 回答 3

3

几个想法...

无论哪种方式,您都希望您的“项目订单字符串”作为一个数组,所以......

var sortOrder = correctItemOrder.Split(new[] { ", " }, StringSplitOptions.None);

然后一个选项是按 sortOrder 中的项目顺序对集合进行排序(这意味着您必须平均遍历集合中每个元素的一半 sortOrder):

var sortedCollection = new Collection<T>(collection.OrderBy(x => Array.IndexOf(sortOrder, x.Name)).ToList());

另一种选择是创建一个 Name => item 的字典,然后遍历 sortOrder,在你去的时候从这个字典中选择项目......

var dict = collection.ToDictionary(x => x.Name);
var sortedCollection = new Collection<T>(sortOrder.Select(x => dict[x]).ToList());

值得注意的是,如果将新项目添加到集合中,而不是 sortOrder,则第一个片段会将它们放在集合的开头,而第二个片段将完全丢弃它们。同样,如果 sortOrder 中存在项目但集合中不存在项目,第一个片段将忽略它们,而第二个片段将引发异常。

编辑:

当然,第三个选项是从 sortOrder 创建字典并使用它。

var dict = sortOrder.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => dict[x.Name]).ToList());

编辑2:

正如 Enigmativity 所指出的,使用查找而不是字典可以让您非常巧妙地处理字典键丢失的情况。

使用此技术的最后一个示例:

var lookup = sortOrder.Select((x, i) => new {x, i}).ToLookup(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => lookup[x.Name].DefaultIfEmpty(Int32.MaxValue).First()).ToList());
于 2013-08-25T11:21:28.590 回答
2

我认为像这样的比较器应该可以完成这项工作:

public interface INamed {
   string Name {get;}
}

public class CustomComparer : Comparer<INamed> {

        Dictionary<string, int> hash;

        public CustomComparer( ) {
           var tokens = "Application5, Application2, Application4, Application3"
                        .Split( ',' )
                        .Select( s => s.Trim( ) )
                        .ToArray( );
           hash = Enumerable.Range(0, tokens.Length)
                            .ToDictionary( i => tokens[i]  );
        }

        public override int Compare( INamed x, INamed y ) {
            return hash[x.Name] - hash[y.Name];
        }

        public static readonly CustomComparer Default = new CustomComparer();
    }

编辑:我看到 Collection 没有自己订购,所以需要构建一个包装器

  class SortableCollection<T> : System.Collections.ObjectModel.Collection<T>
  {
    public SortableCollection() : this(new List<T>()) {}
    public SortableCollection(List<T> list) : base(list) {}
    public virtual void Sort() { ((List<T>)Items).Sort(); }
  }

  class CustomSortableCollection<T> : SortableCollection<T> where T: INamed
  {
    public override void Sort() { 
       ((List<INamed>)Items).Sort(CustomComparer.Default); 
    }
  }

这样,您可以在需要时对集合进行排序:

     your_collection.Sort();
于 2013-08-25T11:21:55.423 回答
1

你可以这样做:

var rank =
    correctItemOrder
        .Split(',')
        .Select((x, n) => new { x = x.Trim(), n, })
        .ToLookup(z => z.x, z => z.x);

var query =
    from i in items
    orderby rank[i.Name]
        .DefaultIfEmpty(int.MaxValue)
        .First()
    select i;

correctItemOrder也处理字符串中的缺失值。

于 2013-08-25T12:27:58.690 回答