104

私は次のクラスを持っています:

[DataContract]
public class Pair<TKey, TValue> : INotifyPropertyChanged, IDisposable
{
    public Pair(TKey key, TValue value)
    {
        Key = key;
        Value = value;
    }

    #region Properties
    [DataMember]
    public TKey Key
    {
        get
        { return m_key; }
        set
        {
            m_key = value;
            OnPropertyChanged("Key");
        }
    }
    [DataMember]
    public TValue Value
    {
        get { return m_value; }
        set
        {
            m_value = value;
            OnPropertyChanged("Value");
        }
    }
    #endregion

    #region Fields
    private TKey m_key;
    private TValue m_value;
    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));
        }
    }

    #endregion

    #region IDisposable Members

    public void Dispose()
    { }

    #endregion
}

ObservableCollection に入れたもの:

ObservableCollection<Pair<ushort, string>> my_collection = 
    new ObservableCollection<Pair<ushort, string>>();

my_collection.Add(new Pair(7, "aaa"));
my_collection.Add(new Pair(3, "xey"));
my_collection.Add(new Pair(6, "fty"));

Q : キーでソートするにはどうすればよいですか?

4

22 に答える 22

39

ここにあるものよりも優れた回答を提供する関連ブログ エントリを見つけました。

http://kiwigis.blogspot.com/2010/03/how-to-sort-obversablecollection.html

アップデート

@romkyns がコメントで指摘した ObservableSortedList は、自動的にソート順を維持します

アイテムをソートされた順序で維持する監視可能なコレクションを実装します。特に、注文の変更につながるアイテム プロパティの変更は正しく処理されます。

ただし、注意事項にも注意してください

関連するインターフェースが比較的複雑であり、ドキュメントが比較的貧弱であるため、バグがある可能性があります ( https://stackoverflow.com/a/5883947/33080を参照)。

于 2011-03-23T06:12:21.127 に答える
28

この簡単な方法を使用できます。

public static void Sort<TSource, TKey>(this Collection<TSource> source, Func<TSource, TKey> keySelector)
{
    List<TSource> sortedList = source.OrderBy(keySelector).ToList();
    source.Clear();
    foreach (var sortedItem in sortedList)
        source.Add(sortedItem);
}

次のように並べ替えることができます。

_collection.Sort(i => i.Key);
于 2011-05-04T21:35:33.880 に答える
23

拡張メソッドを使用して、オブザーバブルをソートし、ソートされた同じオブジェクトを返すことができます。大規模なコレクションの場合は、コレクション変更通知の数に注意してください。

コードを更新してパフォーマンスを改善し(nawfalのおかげ)、執筆時点で他の回答が行っていない重複を処理しました。オブザーバブルは左のソートされた半分と右のソートされていない半分に分割され、最小項目 (ソートされたリストで見つかったもの) がソートされていないパーティションからソートされたパーティションの最後にシフトされます。最悪の場合の O(n)。基本的には選択ソートです (出力については以下を参照してください)。

public static void Sort<T>(this ObservableCollection<T> collection)
        where T : IComparable<T>, IEquatable<T>
    {
        List<T> sorted = collection.OrderBy(x => x).ToList();

        int ptr = 0;
        while (ptr < sorted.Count - 1)
        {
            if (!collection[ptr].Equals(sorted[ptr]))
            {
                int idx = search(collection, ptr+1, sorted[ptr]);
                collection.Move(idx, ptr);
            }
            
            ptr++;
        }
    }

    public static int search<T>(ObservableCollection<T> collection, int startIndex, T other)
            {
                for (int i = startIndex; i < collection.Count; i++)
                {
                    if (other.Equals(collection[i]))
                        return i;
                }
    
                return -1; // decide how to handle error case
            }

使用法: オブザーバーを使用したサンプル (シンプルにするために Person クラスを使用)

    public class Person:IComparable<Person>,IEquatable<Person>
            { 
                public string Name { get; set; }
                public int Age { get; set; }
    
                public int CompareTo(Person other)
                {
                    if (this.Age == other.Age) return 0;
                    return this.Age.CompareTo(other.Age);
                }
    
                public override string ToString()
                {
                    return Name + " aged " + Age;
                }
    
                public bool Equals(Person other)
                {
                    if (this.Name.Equals(other.Name) && this.Age.Equals(other.Age)) return true;
                    return false;
                }
            }
    
          static void Main(string[] args)
            {
                Console.WriteLine("adding items...");
                var observable = new ObservableCollection<Person>()
                {
                    new Person {Name = "Katy", Age = 51},
                    new Person {Name = "Jack", Age = 12},
                    new Person {Name = "Bob", Age = 13},
                    new Person {Name = "Alice", Age = 39},
                    new Person {Name = "John", Age = 14},
                    new Person {Name = "Mary", Age = 41},
                    new Person {Name = "Jane", Age = 20},
                    new Person {Name = "Jim", Age = 39},
                    new Person {Name = "Sue", Age = 5},
                    new Person {Name = "Kim", Age = 19}
                };
    
                //what do observers see?
            
    
observable.CollectionChanged += (sender, e) =>
        {
            Console.WriteLine(
                e.OldItems[0] + " move from " + e.OldStartingIndex + " to " + e.NewStartingIndex);
            int i = 0;
            foreach (var person in sender as ObservableCollection<Person>)
            {
                if (i == e.NewStartingIndex)
                {
                    Console.Write("(" + (person as Person).Age + "),");
                }
                else
                {
                    Console.Write((person as Person).Age + ",");
                }
                
                i++;
            }

            Console.WriteLine();
        };

コレクションがどのようにピボットされるかを示す並べ替えの進行状況の詳細:

Sue aged 5 move from 8 to 0
(5),51,12,13,39,14,41,20,39,19,
Jack aged 12 move from 2 to 1
5,(12),51,13,39,14,41,20,39,19,
Bob aged 13 move from 3 to 2
5,12,(13),51,39,14,41,20,39,19,
John aged 14 move from 5 to 3
5,12,13,(14),51,39,41,20,39,19,
Kim aged 19 move from 9 to 4
5,12,13,14,(19),51,39,41,20,39,
Jane aged 20 move from 8 to 5
5,12,13,14,19,(20),51,39,41,39,
Alice aged 39 move from 7 to 6
5,12,13,14,19,20,(39),51,41,39,
Jim aged 39 move from 9 to 7
5,12,13,14,19,20,39,(39),51,41,
Mary aged 41 move from 9 to 8
5,12,13,14,19,20,39,39,(41),51,

Person クラスは、IComparable と IEquatable の両方を実装します。後者は、発生する変更通知の数を減らすために、コレクションへの変更を最小限に抑えるために使用されます。

  • EDIT 新しいコピーを作成せずに同じコレクションを並べ替えます *

ObservableCollection を返すには、[この実装][1] などを使用して *sortedOC* で .ToObservableCollection を呼び出します。

**** 元の回答 - これにより、新しいコレクションが作成されます **** 以下の doSort メソッドが示すように、linq を使用できます。簡単なコード スニペット:

3:xey 6:fty 7:aaa

または、コレクション自体で拡張メソッドを使用することもできます

var sortedOC = _collection.OrderBy(i => i.Key);

private void doSort()
{
    ObservableCollection<Pair<ushort, string>> _collection = 
        new ObservableCollection<Pair<ushort, string>>();

    _collection.Add(new Pair<ushort,string>(7,"aaa"));
    _collection.Add(new Pair<ushort, string>(3, "xey"));
    _collection.Add(new Pair<ushort, string>(6, "fty"));

    var sortedOC = from item in _collection
                   orderby item.Key
                   select item;

    foreach (var i in sortedOC)
    {
        Debug.WriteLine(i);
    }

}

public class Pair<TKey, TValue>
{
    private TKey _key;

    public TKey Key
    {
        get { return _key; }
        set { _key = value; }
    }
    private TValue _value;

    public TValue Value
    {
        get { return _value; }
        set { _value = value; }
    }
    
    public Pair(TKey key, TValue value)
    {
        _key = key;
        _value = value;

    }

    public override string ToString()
    {
        return this.Key + ":" + this.Value;
    }
}
于 2009-12-22T11:07:31.293 に答える
15

上記の "Richie" のブログでのバブル ソート拡張メソッドのアプローチは気に入りましたが、必ずしもオブジェクト全体を比較するだけでソートする必要はありません。オブジェクトの特定のプロパティでソートしたいことがよくあります。そこで、OrderBy と同じようにキー セレクターを受け入れるように変更して、並べ替えるプロパティを選択できるようにしました。

    public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector)
    {
        if (source == null) return;

        Comparer<TKey> comparer = Comparer<TKey>.Default;

        for (int i = source.Count - 1; i >= 0; i--)
        {
            for (int j = 1; j <= i; j++)
            {
                TSource o1 = source[j - 1];
                TSource o2 = source[j];
                if (comparer.Compare(keySelector(o1), keySelector(o2)) > 0)
                {
                    source.Remove(o1);
                    source.Insert(j, o1);
                }
            }
        }
    }

新しいコレクションを返す代わりに、ObservableCollection の既存のインスタンスをソートすることを除いて、OrderBy を呼び出すのと同じ方法で呼び出します。

ObservableCollection<Person> people = new ObservableCollection<Person>();
...

people.Sort(p => p.FirstName);
于 2012-06-11T18:02:38.817 に答える
12

@ NielWの答えは、実際のインプレースソートのために進むべき道です。使用する必要を回避できるように、わずかに変更されたソリューションを追加したかったのですIComparable

static class Extensions
{
    public static void Sort<TSource, TKey>(this ObservableCollection<TSource> collection, Func<TSource, TKey> keySelector)
    {
        List<TSource> sorted = collection.OrderBy(keySelector).ToList();
        for (int i = 0; i < sorted.Count(); i++)
            collection.Move(collection.IndexOf(sorted[i]), i);
    }
}

これで、ほとんどの LINQ メソッドと同じように呼び出すことができます。

myObservableCollection.Sort(o => o.MyProperty);
于 2015-07-27T16:56:12.203 に答える
8

バリエーションは、選択並べ替えアルゴリズムを使用してコレクションを並べ替える場所です。メソッドを使用して要素を所定の位置に移動しMoveます。各移動は、 (プロパティ名も) でCollectionChangedイベントを発生させます。NotifyCollectionChangedAction.MovePropertyChangedItem[]

このアルゴリズムには、いくつかの優れた特性があります。

  • このアルゴリズムは、安定ソートとして実装できます。
  • コレクション内で移動されるアイテムの数 (CollectionChanged発生したイベントなど) は、ほとんどの場合、挿入ソートやバブル ソートなどの他の同様のアルゴリズムよりも少なくなります。

アルゴリズムは非常に単純です。コレクションは、最小の要素を見つけるために繰り返され、コレクションの先頭に移動されます。このプロセスは、2 番目の要素から始めて、すべての要素が所定の位置に移動されるまで繰り返されます。このアルゴリズムは非常に効率的ではありませんが、ユーザー インターフェイスに表示するものであれば問題ありません。ただし、移動操作の数に関しては、非常に効率的です。

簡単にするために、要素が を実装する必要がある拡張メソッドを次に示しますIComparable<T>。他のオプションはIComparer<T>またはを使用していFunc<T, T, Int32>ます。

public static class ObservableCollectionExtensions {

  public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable<T> {
    if (collection == null)
      throw new ArgumentNullException("collection");

    for (var startIndex = 0; startIndex < collection.Count - 1; startIndex += 1) {
      var indexOfSmallestItem = startIndex;
      for (var i = startIndex + 1; i < collection.Count; i += 1)
        if (collection[i].CompareTo(collection[indexOfSmallestItem]) < 0)
          indexOfSmallestItem = i;
      if (indexOfSmallestItem != startIndex)
        collection.Move(indexOfSmallestItem, startIndex);
    }
  }

}

コレクションの並べ替えは、単純に拡張メソッドを呼び出すだけです。

var collection = new ObservableCollection<String>(...);
collection.Sort();
于 2013-01-17T02:26:58.720 に答える
4

xr280xr answer の拡張メソッドを少し改善するために、オプションの bool パラメータを追加して、並べ替えが降順かどうかを判断しました。その回答へのコメントに、Carlos P による提案も含めました。下記を参照してください。

public static void Sort<TSource, TKey>(this ObservableCollection<TSource> source, Func<TSource, TKey> keySelector, bool desc = false)
    {
        if (source == null) return;

        Comparer<TKey> comparer = Comparer<TKey>.Default;

        for (int i = source.Count - 1; i >= 0; i--)
        {
            for (int j = 1; j <= i; j++)
            {
                TSource o1 = source[j - 1];
                TSource o2 = source[j];
                int comparison = comparer.Compare(keySelector(o1), keySelector(o2));
                if (desc && comparison < 0)
                    source.Move(j, j - 1);
                else if (!desc && comparison > 0)
                    source.Move(j - 1, j);
            }
        }
    }
于 2012-09-22T00:37:00.870 に答える
2

コレクションを常に整理しておく必要がありますか? ペアを取得するとき、それらを常に並べ替える必要がありますか?それとも、数回だけ (おそらく提示のためだけに) 並べ替える必要がありますか? あなたのコレクションはどのくらいの大きさになると思いますか? 使用する魔女の方法を決定するのに役立つ多くの要因があります。

コレクションを常にソートする必要がある場合は、要素を挿入または削除し、挿入速度が問題にならない場合でも、SortedObservableCollection@Gerrie Schenck のようなものを実装するか、この実装を確認してください。

数回だけコレクションをソートする必要がある場合は、次を使用します。

my_collection.OrderBy(p => p.Key);

これはコレクションをソートするのに時間がかかりますが、それでも、あなたが何をしているかによっては、それが最善の解決策かもしれません.

于 2009-12-22T11:10:05.137 に答える
2

私の現在の回答はすでに最も多くの票を獲得していますが、これを行うためのより優れた、より現代的な方法を見つけました。

class MyObject 
{
      public int id { get; set; }
      public string title { get; set; }
}

ObservableCollection<MyObject> myCollection = new ObservableCollection<MyObject>();

//add stuff to collection
// .
// .
// .

myCollection = new ObservableCollection<MyObject>(
    myCollection.OrderBy(n => n.title, Comparer<string>.Create(
    (x, y) => (Utils.Utils.LogicalStringCompare(x, y)))));
于 2016-09-07T16:07:18.180 に答える
1

新しいクラスをSortedObservableCollection作成し、それを派生させてObservableCollection実装しIComparable<Pair<ushort, string>>ます。

于 2009-12-22T10:24:27.490 に答える
1

1 つの方法は、List に変換してから Sort() を呼び出し、比較デリゲートを提供することです。何かのようなもの:-

(未テスト)

my_collection.ToList().Sort((left, right) => left == right ? 0 : (left > right ? -1 : 1));
于 2009-12-22T11:13:00.730 に答える
1

私の場合、これらの答えはどれもうまくいきませんでした。バインディングを台無しにするか、悪夢のような追加のコーディングが必要になるか、答えが壊れているかのいずれかです。だから、ここに私が考えたさらに別の簡単な答えがあります。コードが大幅に減り、this.sort タイプのメソッドが追加された同じ監視可能なコレクションのままです。このようにしてはいけない理由があるかどうか教えてください (効率など)?

public class ScoutItems : ObservableCollection<ScoutItem>
{
    public void Sort(SortDirection _sDir, string _sItem)
    {
             //TODO: Add logic to look at _sItem and decide what property to sort on
            IEnumerable<ScoutItem> si_enum = this.AsEnumerable();

            if (_sDir == SortDirection.Ascending)
            {
                si_enum = si_enum.OrderBy(p => p.UPC).AsEnumerable();
            } else
            {
                si_enum = si_enum.OrderByDescending(p => p.UPC).AsEnumerable();
            }

            foreach (ScoutItem si in si_enum)
            {
                int _OldIndex = this.IndexOf(si);
                int _NewIndex = si_enum.ToList().IndexOf(si);
                this.MoveItem(_OldIndex, _NewIndex);
            }
      }
}

... ScoutItem は私の public クラスです。ずっとシンプルに見えました。追加の利点: 実際に機能し、バインディングを台無しにしたり、新しいコレクションを返したりすることはありません。

于 2013-10-12T20:48:58.880 に答える
1

これは私が OC 拡張機能で行うことです:

    /// <summary>
    /// Synches the collection items to the target collection items.
    /// This does not observe sort order.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source">The items.</param>
    /// <param name="updatedCollection">The updated collection.</param>
    public static void SynchCollection<T>(this IList<T> source, IEnumerable<T> updatedCollection)
    {
        // Evaluate
        if (updatedCollection == null) return;

        // Make a list
        var collectionArray = updatedCollection.ToArray();

        // Remove items from FilteredViewItems not in list
        source.RemoveRange(source.Except(collectionArray));

        // Add items not in FilteredViewItems that are in list
        source.AddRange(collectionArray.Except(source));
    }

    /// <summary>
    /// Synches the collection items to the target collection items.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="source">The source.</param>
    /// <param name="updatedCollection">The updated collection.</param>
    /// <param name="canSort">if set to <c>true</c> [can sort].</param>
    public static void SynchCollection<T>(this ObservableCollection<T> source,
        IList<T> updatedCollection, bool canSort = false)
    {
        // Synch collection
        SynchCollection(source, updatedCollection.AsEnumerable());

        // Sort collection
        if (!canSort) return;

        // Update indexes as needed
        for (var i = 0; i < updatedCollection.Count; i++)
        {
            // Index of new location
            var index = source.IndexOf(updatedCollection[i]);
            if (index == i) continue;

            // Move item to new index if it has changed.
            source.Move(index, i);
        }
    }
于 2015-12-12T18:08:20.067 に答える
1

なんてこった、私もすぐに石畳の答えを投げます...それはここの他のいくつかの実装に少し似ていますが、私はそれをどこにでも追加します:

(かろうじてテストしましたが、恥ずかしくないことを願っています)

最初にいくつかの目標を述べましょう (私の仮定):

1)ObservableCollection<T>通知などを維持するために、所定の場所に並べ替える必要があります。

2) ひどく非効率であってはなりません (つまり、標準的な「良好な」選別効率に近いもの)。

public static class Ext
{
    public static void Sort<T>(this ObservableCollection<T> src)
        where T : IComparable<T>
    {
        // Some preliminary safety checks
        if(src == null) throw new ArgumentNullException("src");
        if(!src.Any()) return;

        // N for the select,
        // + ~ N log N, assuming "smart" sort implementation on the OrderBy
        // Total: N log N + N (est)
        var indexedPairs = src
            .Select((item,i) => Tuple.Create(i, item))
            .OrderBy(tup => tup.Item2);
        // N for another select
        var postIndexedPairs = indexedPairs
            .Select((item,i) => Tuple.Create(i, item.Item1, item.Item2));
        // N for a loop over every element
        var pairEnum = postIndexedPairs.GetEnumerator();
        pairEnum.MoveNext();
        for(int idx = 0; idx < src.Count; idx++, pairEnum.MoveNext())
        {
            src.RemoveAt(pairEnum.Current.Item1);
            src.Insert(idx, pairEnum.Current.Item3);            
        }
        // (very roughly) Estimated Complexity: 
        // N log N + N + N + N
        // == N log N + 3N
    }
}
于 2013-03-26T15:39:25.800 に答える
1

これは私にとってはうまくいきました。ずっと前にどこかで見つけました。

// SortableObservableCollection
public class SortableObservableCollection<T> : ObservableCollection<T>
    {
        public SortableObservableCollection(List<T> list)
            : base(list)
        {
        }

        public SortableObservableCollection()
        {
        }

        public void Sort<TKey>(Func<T, TKey> keySelector, System.ComponentModel.ListSortDirection direction)
        {
            switch (direction)
            {
                case System.ComponentModel.ListSortDirection.Ascending:
                    {
                        ApplySort(Items.OrderBy(keySelector));
                        break;
                    }
                case System.ComponentModel.ListSortDirection.Descending:
                    {
                        ApplySort(Items.OrderByDescending(keySelector));
                        break;
                    }
            }
        }

        public void Sort<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer)
        {
            ApplySort(Items.OrderBy(keySelector, comparer));
        }

        private void ApplySort(IEnumerable<T> sortedItems)
        {
            var sortedItemsList = sortedItems.ToList();

            foreach (var item in sortedItemsList)
            {
                Move(IndexOf(item), sortedItemsList.IndexOf(item));
            }
        }
    }

使用法:

MySortableCollection.Sort(x => x, System.ComponentModel.ListSortDirection.Ascending);
于 2015-10-05T13:03:45.683 に答える
0

他のソリューションから多くのことを学びましたが、いくつかの問題が見つかりました。まず、大きなリストではかなり遅くなる傾向がある IndexOf に依存するものがあります。次に、ObservableCollection には EF エンティティがあり、Remove を使用すると、一部の外部キー プロパティが破損しているように見えました。多分私は何か間違ったことをしています。

とにかく、削除/挿入の代わりに移動を使用できますが、パフォーマンスの修正でいくつかの問題が発生します。

パフォーマンスの問題を解決するために、並べ替えられた IndexOf 値を使用して辞書を作成します。ディクショナリを最新の状態に保ち、エンティティ プロパティを保持するには、他のソリューションで実装されている 1 つではなく、2 つの移動で実装されたスワップを使用します。

1 回の移動で要素のインデックスが位置間でシフトされ、IndexOf ディクショナリが無効になります。スワップを実装するために2番目の移動を追加すると、場所が復元されます。

public static void Sort<TSource, TKey>(this ObservableCollection<TSource> collection, Func<TSource, TKey> keySelector)
{
    List<TSource> sorted = collection.OrderBy(keySelector).ToList();
    Dictionary<TSource, int> indexOf = new Dictionary<TSource, int>();

    for (int i = 0; i < sorted.Count; i++)
        indexOf[sorted[i]] = i;

    int idx = 0;
    while (idx < sorted.Count)
        if (!collection[idx].Equals(sorted[idx])) {
            int newIdx = indexOf[collection[idx]]; // where should current item go?
            collection.Move(newIdx, idx); // move whatever's there to current location
            collection.Move(idx + 1, newIdx); // move current item to proper location
        }
        else {
            idx++;
        }
}
于 2020-05-15T08:42:49.353 に答える
-3
var collection = new ObservableCollection<int>();

collection.Add(7);
collection.Add(4);
collection.Add(12);
collection.Add(1);
collection.Add(20);

// ascending
collection = new ObservableCollection<int>(collection.OrderBy(a => a));

// descending
collection = new ObservableCollection<int>(collection.OrderByDescending(a => a));
于 2013-03-18T20:03:43.970 に答える