[c#] 관찰 가능한 컬렉션을 어떻게 정렬합니까?

다음 수업이 있습니다.

[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 : 키별로 어떻게 정렬합니까?



답변

관찰 가능 항목을 정렬하고 정렬 된 동일한 객체를 반환하는 작업은 확장 메서드를 사용하여 수행 할 수 있습니다. 더 큰 컬렉션의 경우 컬렉션 변경 알림 수를주의하세요.

성능을 향상시키고 중복을 처리하기 위해 코드를 업데이트했습니다 (원래 데이터 예제에서는 잘 작동했지만 원본의 성능 저하를 강조한 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을 모두 구현합니다. 후자는 발생하는 변경 알림의 수를 줄이기 위해 컬렉션의 변경을 최소화하는 데 사용됩니다.

  • 편집 새 복사본을 만들지 않고 동일한 컬렉션을 정렬합니다. *

ObservableCollection을 반환하려면 [this implementation] [1]과 같이 * sortedOC *에서 .ToObservableCollection을 호출합니다.

**** orig answer-이것은 새로운 컬렉션을 생성합니다 **** 아래의 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;
    }
}


답변

이 간단한 확장은 저에게 아름답게 작동했습니다. 난 그냥 그 확인했다 MyObject이었다 IComparable. 정렬 방법의 관찰 컬렉션 호출 될 때 MyObjectsCompareTo의 방법은 MyObject내 논리 정렬 메소드를 호출하는 호출됩니다. 여기에 게시 된 나머지 답변의 모든 종소리와 휘파람이 없지만 정확히 필요한 것입니다.

static class Extensions
{
    public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
    {
        List<T> sorted = collection.OrderBy(x => x).ToList();
        for (int i = 0; i < sorted.Count(); i++)
            collection.Move(collection.IndexOf(sorted[i]), i);
    }
}

public class MyObject: IComparable
{
    public int CompareTo(object o)
    {
        MyObject a = this;
        MyObject b = (MyObject)o;
        return Utils.LogicalStringCompare(a.Title, b.Title);
    }

    public string Title;

}
  .
  .
  .
myCollection = new ObservableCollection<MyObject>();
//add stuff to collection
myCollection.Sort();


답변

여기에있는 것보다 더 나은 답변을 제공하는 관련 블로그 항목을 찾았습니다.

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

최신 정보

ObservableSortedList 코멘트 아웃 @romkyns 포인트가 자동으로 정렬 순서를 유지하고있다.

항목을 정렬 된 순서로 유지하는 관찰 가능한 컬렉션을 구현합니다. 특히, 주문 변경을 초래하는 항목 속성 변경은 올바르게 처리됩니다.

그러나 비고도 참고

관련된 인터페이스의 상대적 복잡성과 상대적으로 열악한 문서로 인해 버그가있을 수 있습니다 ( https://stackoverflow.com/a/5883947/33080 참조 ).


답변

이 간단한 방법을 사용할 수 있습니다.

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

자세한 내용 : http://jaider.net/2011-05-04/sort-a-observablecollection/


답변

WPF는ListCollectionView 클래스를 사용하여 기본적으로 라이브 정렬을 제공합니다 .

public ObservableCollection<string> MyStrings { get; set; }
private ListCollectionView _listCollectionView;
private void InitializeCollection()
{
    MyStrings = new ObservableCollection<string>();
    _listCollectionView = CollectionViewSource.GetDefaultView(MyStrings)
              as ListCollectionView;
    if (_listCollectionView != null)
    {
        _listCollectionView.IsLiveSorting = true;
        _listCollectionView.CustomSort = new
                CaseInsensitiveComparer(CultureInfo.InvariantCulture);
    }
}

이 초기화가 완료되면 더 이상 할 일이 없습니다. 수동 정렬에 비해 장점은 ListCollectionView가 개발자에게 투명한 방식으로 모든 무거운 작업을 수행한다는 것입니다. 새 항목은 올바른 정렬 순서로 자동 배치됩니다. 파생되는 모든 클래스IComparerT 는 사용자 지정 정렬 속성에 적합합니다.

문서 및 기타 기능 은 ListCollectionView 를 참조하십시오 .


답변

위의 “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);


답변

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