C # sortowalna kolekcja, która umożliwia zduplikowane klucze

97

Piszę program ustawiający kolejność, w jakiej różne obiekty będą się pojawiać w raporcie. Sekwencja to pozycja Y (komórka) w arkuszu kalkulacyjnym Excel.

Część demonstracyjna kodu znajduje się poniżej. To, co chcę osiągnąć, to mieć kolekcję, która pozwoli mi dodać wiele obiektów i mogę uzyskać posortowaną kolekcję na podstawie sekwencji

SortedList list = new SortedList();

Header h = new Header();
h.XPos = 1;
h.name = "Header_1";
list.Add(h.XPos, h);

h = new Header();
h.XPos = 1;
h.name = "Header_2";
list.Add(h.XPos, h);

Wiem, że na SortedListto nie pozwolę i szukałem alternatywy. Nie chcę eliminować duplikatów i już próbowałem List<KeyValuePair<int, object>>.

Dzięki.

Mayur Kotlikar
źródło
1
Czy kolekcja musi obsługiwać wstawianie / usuwanie po otrzymaniu początkowej listy członków?
Ani
2
Co nie zadziałało, kiedy próbowałeś List?
diceguyd 30
Nie chcę tylko sortować i pobierać obiektu. Ale raczej chcę uzyskać całą posortowaną listę. W poniższym przykładzie oba obiekty Header powinny istnieć iw kolejności jeden pod drugim. Jeśli dodam kolejny obiekt Header z XPos = 2, powinienem wtedy mieć na liście 3 obiekty, 2 obiekty z XPos = 1 i trzeci jako XPos = 2
Mayur Kotlikar
Tylko uwaga: kiedy napotykam tego typu sytuację, okazuje się, że ogólna lista w połączeniu z mało znanym zachowaniem BinarySearch dla elementów nieznalezionych działa cuda.
J Trana,

Odpowiedzi:

79

Użyj własnego IComparer!

Jak już stwierdzono w niektórych innych odpowiedziach, powinieneś użyć własnej klasy porównującej. W tym celu używam ogólnej klasy IComparer, która działa ze wszystkim, co implementuje IComparable:

/// <summary>
/// Comparer for comparing two keys, handling equality as beeing greater
/// Use this Comparer e.g. with SortedLists or SortedDictionaries, that don't allow duplicate keys
/// </summary>
/// <typeparam name="TKey"></typeparam>
public class DuplicateKeyComparer<TKey>
                :
             IComparer<TKey> where TKey : IComparable
{
    #region IComparer<TKey> Members

    public int Compare(TKey x, TKey y)
    {
        int result = x.CompareTo(y);

        if (result == 0)
            return 1;   // Handle equality as beeing greater
        else
            return result;
    }

    #endregion
}

Użyjesz go podczas tworzenia nowej SortedList, SortedDictionary itp:

SortedList<int, MyValueClass> slist = new SortedList<int, MyValueClass>(new DuplicateKeyComparer<int>());

Tutaj int jest kluczem, który można powielić.

Knasterbax
źródło
43
Ale nie będziesz w stanie usunąć z niego żadnego klucza.
Shashwat
11
Tak, zgadza się, Shachwat! Nie można użyć Remove (klucz) ani IndexOfKey (klucz), ponieważ funkcja porównująca nigdy nie zwraca 0, aby sygnalizować równość klucza. Ale możesz RemoveAt (index), aby usunąć elementy, jeśli masz ich indeks.
Knasterbax
1
Napotkałem również ten sam problem, którego użyłem SortedDictionary. Umożliwia również usunięcie.
Shashwat
10
Zauważ, że w ten sposób łamiesz refleksyjność swojej osoby porównującej. Może (i będzie) zepsuć rzeczy w BCL.
ghord
2
to powinno faktycznie zwrócić -1, aby zachować porządek
M.kazem Akhgary
16

Możesz bezpiecznie używać List <>. List ma metodę Sort, której przeciążenie akceptuje IComparer. Możesz utworzyć własną klasę sortowania jako. Oto przykład:

private List<Curve> Curves;
this.Curves.Sort(new CurveSorter());

public class CurveSorter : IComparer<Curve>
{
    public int Compare(Curve c1, Curve c2)
    {
        return c2.CreationTime.CompareTo(c1.CreationTime);
    }
}
Dipti Mehta
źródło
1
Nie chcę tylko sortować i pobierać obiektu. Ale raczej chcę uzyskać całą posortowaną listę. W poniższym przykładzie oba obiekty Header powinny istnieć iw kolejności jeden pod drugim. Jeśli dodam kolejny obiekt Header z XPos = 2, powinienem mieć na liście 3 obiekty, 2 obiekty z XPos = 1 i trzeci jako XPos = 2
Mayur Kotlikar
1
w porządku, masz na myśli to, że w momencie wstawiania elementu na listę, powinien on być wstawiony na właściwej pozycji zgodnie z sortowaniem. Proszę, popraw mnie, jeśli się mylisz. Pozwól mi spojrzeć, wrócę za chwilę
Dipti Mehta
Należy zauważyć, że List <T> .Sort używa wielu algorytmów sortowania w zależności od rozmiaru kolekcji i nie wszystkie z nich są sortowaniami stabilnymi. Dlatego obiekty dodane do kolekcji, które są porównywane z odpowiednikami, mogą nie pojawiać się w kolejności, w jakiej zostały dodane.
cichy ton
Wybrałem tę opcję, aby przestać tworzyć nadmierne ilości KeyValuePairs dzięki zastosowaniu funkcji redukcji do słownika
Chris Marisic
10

Używam następujących:

public class TupleList<T1, T2> : List<Tuple<T1, T2>> where T1 : IComparable
{
    public void Add(T1 item, T2 item2)
    {
        Add(new Tuple<T1, T2>(item, item2));
    }

    public new void Sort()
    {
        Comparison<Tuple<T1, T2>> c = (a, b) => a.Item1.CompareTo(b.Item1);
        base.Sort(c);
    }

}

Mój przypadek testowy:

[TestMethod()]
    public void SortTest()
    {
        TupleList<int, string> list = new TupleList<int, string>();
        list.Add(1, "cat");
        list.Add(1, "car");
        list.Add(2, "dog");
        list.Add(2, "door");
        list.Add(3, "elephant");
        list.Add(1, "coconut");
        list.Add(1, "cab");
        list.Sort();
        foreach(Tuple<int, string> tuple in list)
        {
            Console.WriteLine(string.Format("{0}:{1}", tuple.Item1,tuple.Item2));
        }
        int expected_first = 1;
        int expected_last = 3;
        int first = list.First().Item1;  //requires using System.Linq
        int last = list.Last().Item1;    //requires using System.Linq
        Assert.AreEqual(expected_first, first);
        Assert.AreEqual(expected_last, last);
    }

Wyjście:

1:cab
1:coconut
1:car
1:cat
2:door
2:dog
3:elephant
użytkownik450
źródło
Tuple nie jest dostępny we wszystkich wersjach .NET, ale można go zastąpić KeyValuePair <K, V>
Reuben
6

Problem polega na tym, że projekt struktury danych nie spełnia wymagań: konieczne jest przechowywanie kilku nagłówków dla tego samego XPos. Dlatego SortedList<XPos, value>nie powinno mieć wartości Header, ale wartość List<Header>. To prosta i niewielka zmiana, ale rozwiązuje wszystkie problemy i pozwala uniknąć tworzenia nowych problemów, takich jak inne sugerowane rozwiązania (patrz wyjaśnienie poniżej):

using System;
using System.Collections.Generic;

namespace TrySortedList {
  class Program {

    class Header {
      public int XPos;
      public string Name;
    }

    static void Main(string[] args) {
      SortedList<int, List<Header>> sortedHeaders = new SortedList<int,List<Header>>();
      add(sortedHeaders, 1, "Header_1");
      add(sortedHeaders, 1, "Header_2");
      add(sortedHeaders, 2, "Header_3");
      foreach (var headersKvp in sortedHeaders) {
        foreach (Header header in headersKvp.Value) {
          Console.WriteLine(header.XPos + ": " + header.Name);
        }
      }
    }

    private static void add(SortedList<int, List<Header>> sortedHeaders, int xPos, string name) {
      List<Header> headers;
      if (!sortedHeaders.TryGetValue(xPos, out headers)){
        headers = new List<Header>();
        sortedHeaders[xPos] = headers;
      }
      headers.Add(new Header { XPos = xPos, Name = name });
    }
  }
}

Output:
1: Header_1
1: Header_2
2: Header_3

Zwróć uwagę, że dodanie „zabawnego” klucza, np. Dodanie losowej liczby lub udawanie, że 2 punkty XP o tej samej wartości są różne, prowadzi do wielu innych problemów. Na przykład usunięcie określonego nagłówka staje się trudne lub nawet niemożliwe.

Należy również zauważyć, że wydajność sortowania jest znacznie lepsza, jeśli tylko kilka List<Header>musi być sortowanych niż wszystkie Header. Przykład: Jeśli jest 100 punktów XP i każdy ma 100 nagłówków, Headernależy posortować 10000, a nie 100List<Header> .

Oczywiście również to rozwiązanie ma wadę: jeśli istnieje wiele XPo z tylko jednym nagłówkiem, trzeba utworzyć tyle list, co jest pewnym narzutem.

Peter Huber
źródło
To najprostsze rozwiązanie. Sprawdź także SortedDictionary, jest podobny, w niektórych przypadkach szybszy.
Hogan,
To naprawdę dobre rozwiązanie. Można by dość łatwo umieścić tę funkcjonalność w jakimś niestandardowym obiekcie kolekcji i byłoby to całkiem przydatne. Dobra myśl, dzięki za udostępnienie Petera!
Adam P
5

Najprostsze rozwiązanie (w porównaniu do wszystkich powyższych): użyj SortedSet<T>, akceptuje IComparer<SortableKey>klasę, a następnie zaimplementuj metodę Compare w ten sposób:

public int Compare(SomeClass x, SomeClass y)
{
    var compared = x.SomeSortableKeyTypeField.CompareTo(y.SomeSortableKeyTypeField);
    if (compared != 0)
        return compared;

    // to allow duplicates
    var hashCodeCompare = x.GetHashCode().CompareTo(y.GetHashCode());
    if (hashCodeCompare != 0)
        return hashCodeCompare;

    if (Object.ReferenceEquals(x, y))
        return 0;

    // for weird duplicate hashcode cases, throw as below or implement your last chance comparer
    throw new ComparisonFailureException();

}
knocte
źródło
4
Użyłem SortedSet <T>, a T miał swój własny zwiększający się identyfikator int, który był zwiększany przy każdej instancji, aby zapewnić, że każde T jest unikalne, nawet jeśli inne pola są takie same.
Skychan
3
GetHashCode do porównania jest niebezpieczny. Może prowadzić do nieoczekiwanego fałszywego duplikatu. Może to działać przez większość czasu, ale nigdy nie użyłbym tego do niczego poważnego.
Hogan,
4

Bardzo dziękuję za Twoją pomoc. Szukając dalej, znalazłem to rozwiązanie. (Dostępne na Stackoverflow.com w innym pytaniu)

Najpierw stworzyłem klasę, która zawierałaby moje obiekty dla klas (nagłówki, stopki itp.)

public class MyPosition
{
    public int Position { get; set; }
    public object MyObjects{ get; set; }
}

Więc ta klasa ma trzymać na obiektach, a PosX każdego obiektu przyjmuje wartość int Position

List<MyPosition> Sequence= new List<MyPosition>();
Sequence.Add(new MyPosition() { Position = 1, Headerobject });
Sequence.Add(new MyPosition() { Position = 2, Headerobject1 });
Sequence.Add(new MyPosition() { Position = 1, Footer });

League.Sort((PosA, PosB) => PosA.Position.CompareTo(PosB.Position));

Ostatecznie otrzymuję posortowaną listę „Sekwencja”.

Mayur Kotlikar
źródło
2

Czy próbowałeś Lookup<TKey, TElement>, aby umożliwić zduplikowane klucze http://msdn.microsoft.com/en-us/library/bb460184.aspx

Nasmi Sabeer
źródło
Dzięki. Mój problem polega na tym, że obiekty nie będą tylko jednego typu (po prostu nie będą to nagłówki), te mogą się różnić (powiedzmy stopka, pasek boczny itp.), Ale każdy będzie miał XPos
Mayur Kotlikar
LookupUważam też, że nie ma publicznego konstruktora . Jakiś dobry sposób na obejście tego?
Jeff B,
1
@JeffBridgman będziesz musiał polegać na Linq. Możesz to zrobić ToLookupna dowolnym IEnumerable<T>.
nawfal
8
Tak, zezwala na zduplikowane klucze, ale nic nie jest sortowane!
Roman Starkov
2

Możesz użyć SortedList, użyć swojej wartości dla TKey i int (count) dla TValue.

Oto przykład: funkcja, która sortuje litery słowa.

    private string sortLetters(string word)
    {
        var input = new System.Collections.Generic.SortedList<char, int>();

        foreach (var c in word.ToCharArray())
        {
            if (input.ContainsKey(c))
                input[c]++;
            else
                input.Add(c, 1);
        }

        var output = new StringBuilder();

        foreach (var kvp in input)
        {
            output.Append(kvp.Key, kvp.Value);
        }

        string s;

        return output.ToString();

    }
Patrice Calvé
źródło
2

Ta klasa kolekcji zachowa duplikaty i wstawi kolejność sortowania dla duplikatów. Sztuczka polega na oznaczeniu elementów unikalną wartością podczas ich wstawiania, aby zachować stabilną kolejność sortowania. Następnie zawijamy to wszystko w interfejsie ICollection.

public class SuperSortedSet<TValue> : ICollection<TValue>
{
    private readonly SortedSet<Indexed<TValue>> _Container;
    private int _Index = 0;
    private IComparer<TValue> _Comparer;

    public SuperSortedSet(IComparer<TValue> comparer)
    {
        _Comparer = comparer;
        var c2 = new System.Linq.Comparer<Indexed<TValue>>((p0, p1) =>
        {
            var r = _Comparer.Compare(p0.Value, p1.Value);
            if (r == 0)
            {
                if (p0.Index == -1
                    || p1.Index == -1)
                    return 0;

                return p0.Index.CompareTo(p1.Index);

            }
            else return r;
        });
        _Container = new SortedSet<Indexed<TValue>>(c2);
    } 

    public IEnumerator<TValue> GetEnumerator() { return _Container.Select(p => p.Value).GetEnumerator(); }

    IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }

    public void Add(TValue item) { _Container.Add(Indexed.Create(_Index++, item)); }

    public void Clear() { _Container.Clear();}

    public bool Contains(TValue item) { return _Container.Contains(Indexed.Create(-1,item)); }

    public void CopyTo(TValue[] array, int arrayIndex)
    {
        foreach (var value in this)
        {
            if (arrayIndex >= array.Length)
            {
                throw new ArgumentException("Not enough space in array");
            }
            array[arrayIndex] = value;
            arrayIndex++;
        }
    }

    public bool Remove(TValue item) { return _Container.Remove(Indexed.Create(-1, item)); }

    public int Count {
        get { return _Container.Count; }
    }
    public bool IsReadOnly {
        get { return false; }
    }
}

klasa testowa

[Fact]
public void ShouldWorkWithSuperSortedSet()
{
    // Sort points according to X
    var set = new SuperSortedSet<Point2D>
        (new System.Linq.Comparer<Point2D>((p0, p1) => p0.X.CompareTo(p1.X)));

    set.Add(new Point2D(9,10));
    set.Add(new Point2D(1,25));
    set.Add(new Point2D(11,-10));
    set.Add(new Point2D(2,99));
    set.Add(new Point2D(5,55));
    set.Add(new Point2D(5,23));
    set.Add(new Point2D(11,11));
    set.Add(new Point2D(21,12));
    set.Add(new Point2D(-1,76));
    set.Add(new Point2D(16,21));

    var xs = set.Select(p=>p.X).ToList();
    xs.Should().BeInAscendingOrder();
    xs.Count.Should()
       .Be(10);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,5,9,11,11,16,21});

    set.Remove(new Point2D(5,55));
    xs = set.Select(p=>p.X).ToList();
    xs.Count.Should()
       .Be(9);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,9,11,11,16,21});

    set.Remove(new Point2D(5,23));
    xs = set.Select(p=>p.X).ToList();
    xs.Count.Should()
       .Be(8);
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,9,11,11,16,21});

    set.Contains(new Point2D(11, 11))
       .Should()
       .BeTrue();

    set.Contains(new Point2D(-1, 76))
        .Should().BeTrue();

    // Note that the custom compartor function ignores the Y value
    set.Contains(new Point2D(-1, 66))
        .Should().BeTrue();

    set.Contains(new Point2D(27, 66))
        .Should().BeFalse();

}

Struktura tagowania

public struct Indexed<T>
{
    public int Index { get; private set; }
    public T Value { get; private set; }
    public Indexed(int index, T value) : this()
    {
        Index = index;
        Value = value;
    }

    public override string ToString()
    {
        return "(Indexed: " + Index + ", " + Value.ToString () + " )";
    }
}

public class Indexed
{
    public static Indexed<T> Create<T>(int indexed, T value)
    {
        return new Indexed<T>(indexed, value);
    }
}

Pomocnik porównujący lambda

public class Comparer<T> : IComparer<T>
{
    private readonly Func<T, T, int> _comparer;

    public Comparer(Func<T, T, int> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");
        _comparer = comparer;
    }

    public int Compare(T x, T y)
    {
        return _comparer(x, y);
    }
}
bradgonesurfing
źródło
1

Problem polega na tym, że jako klucza używasz czegoś, co nie jest kluczem (ponieważ występuje wiele razy).

Więc jeśli masz prawdziwe współrzędne, może powinieneś wziąć Pointklucz jako klucz do swojej SortedList.

Lub możesz utworzyć List<List<Header>>gdzie twój pierwszy indeks listy definiuje pozycję x, a lista wewnętrzna indeksuje pozycję y (lub odwrotnie, jeśli chcesz).

Oliver
źródło
Klucz może mieć wiele instancji, o ile nie jest kluczem podstawowym. Przynajmniej tak mi powiedzieli na zajęciach z baz danych, które wziąłem.
połączono
1
Ta odpowiedź jest trochę krótka, ale dobrze wyjaśnia problem i dostarcza poprawnego rozwiązania, tj. Za pomocą SortedList <int, List <Header>>. Dzięki temu nagłówki są posortowane i można przechowywać wiele nagłówków w tych samych punktach xPos. Aby uzyskać przykładowy kod, poszukaj mojej odpowiedzi. Zaliczyłem jedną z tych odpowiedzi, ponieważ wskazuje ona właściwy kierunek. Proszę o dodanie 1 mojej odpowiedzi, jeśli uważasz, że jest to pomocne.
Peter Huber
1

Kluczem do tego (przeznaczonym dla kalamburów) jest stworzenie IComparableklasy bazowej, która zachowuje równość i haszowanie, ale nigdy nie porównuje do 0, jeśli nie jest równa. Można to zrobić i można to stworzyć z kilkoma bonusami - stabilne sortowanie (to znaczy wartości dodane do posortowanej listy jako pierwsze zachowają swoją pozycję), orazToString() mogą po prostu zwrócić rzeczywistą wartość ciągu klucza.

Oto klucz struct, który powinien załatwić sprawę:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace System
{
    /// <summary>
    /// Defined in Totlsoft.Util.
    /// A key that will always be unique but compares
    /// primarily on the Key property, which is not required
    /// to be unique.
    /// </summary>
    public struct StableKey : IComparable<StableKey>, IComparable
    {
        private static long s_Next;
        private long m_Sequence;
        private IComparable m_Key;

        /// <summary>
        /// Defined in Totlsoft.Util.
        /// Constructs a StableKey with the given IComparable key.
        /// </summary>
        /// <param name="key"></param>
        public StableKey( IComparable key )
        {
            if( null == key )
                throw new ArgumentNullException( "key" );

            m_Sequence = Interlocked.Increment( ref s_Next );
            m_Key = key;
        }

        /// <summary>
        /// Overridden. True only if internal sequence and the
        /// Key are equal.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals( object obj )
        {
            if( !( obj is StableKey ) )
                return false;

            var dk = (StableKey)obj;

            return m_Sequence.Equals( dk.m_Sequence ) &&
                Key.Equals( dk.Key );
        }

        /// <summary>
        /// Overridden. Gets the hash code of the internal
        /// sequence and the Key.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return m_Sequence.GetHashCode() ^ Key.GetHashCode();
        }

        /// <summary>
        /// Overridden. Returns Key.ToString().
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return Key.ToString();
        }

        /// <summary>
        /// The key that will be compared on.
        /// </summary>
        public IComparable Key
        {
            get
            {
                if( null == m_Key )
                    return 0;

                return m_Key;
            }
        }

        #region IComparable<StableKey> Members

        /// <summary>
        /// Compares this Key property to another. If they
        /// are the same, compares the incremented value.
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public int CompareTo( StableKey other )
        {
            var cmp = Key.CompareTo( other.Key );
            if( cmp == 0 )
                cmp = m_Sequence.CompareTo( other.m_Sequence );

            return cmp;
        }

        #endregion

        #region IComparable Members

        int IComparable.CompareTo( object obj )
        {
            return CompareTo( (StableKey)obj );
        }

        #endregion
    }
}
Bruce Pierson
źródło
To fajny pomysł. Zapakowałem koncepcję w niestandardową kolekcję ICollection. Zobacz stackoverflow.com/a/21625939/158285
bradgonesurfing
0

Linq.Lookup jest fajny iw ogóle, ale jeśli twoim celem jest po prostu zapętlenie "kluczy", pozwalając na ich powielenie, możesz użyć tej struktury:

List<KeyValuePair<String, String>> FieldPatterns = new List<KeyValuePair<string, string>>() {
   new KeyValuePair<String,String>("Address","CommonString"),
   new KeyValuePair<String,String>("Username","UsernamePattern"),
   new KeyValuePair<String,String>("Username","CommonString"),
};

Następnie możesz napisać:

foreach (KeyValuePair<String,String> item in FieldPatterns)
{
   //use item.Key and item.Value
}

HTH

Michael Bahig
źródło
0

Sztuczka polega na tym, aby ulepszyć swój obiekt za pomocą unikalnego klucza. Zobacz poniższy test, który kończy się powodzeniem. Chcę, aby moje punkty były posortowane według wartości X. Samo użycie nagiego Point2D w mojej funkcji porównawczej spowoduje, że punkty o tej samej wartości X zostaną wyeliminowane. Więc opakowuję Point2D w klasę tagowania o nazwie Indexed.

[Fact]
public void ShouldBeAbleToUseCustomComparatorWithSortedSet()
{
    // Create comparer that compares on X value but when X
    // X values are uses the index
    var comparer = new 
        System.Linq.Comparer<Indexed<Point2D>>(( p0, p1 ) =>
        {
            var r = p0.Value.X.CompareTo(p1.Value.X);
            return r == 0 ? p0.Index.CompareTo(p1.Index) : r;
        });

    // Sort points according to X
    var set = new SortedSet<Indexed<Point2D>>(comparer);

    int i=0;

    // Create a helper function to wrap each point in a unique index
    Action<Point2D> index = p =>
    {
        var ip = Indexed.Create(i++, p);
        set.Add(ip);
    };

    index(new Point2D(9,10));
    index(new Point2D(1,25));
    index(new Point2D(11,-10));
    index(new Point2D(2,99));
    index(new Point2D(5,55));
    index(new Point2D(5,23));
    index(new Point2D(11,11));
    index(new Point2D(21,12));
    index(new Point2D(-1,76));
    index(new Point2D(16,21));
    set.Count.Should()
       .Be(10);
    var xs = set.Select(p=>p.Value.X).ToList();
    xs.Should()
      .BeInAscendingOrder();
    xs.ShouldBeEquivalentTo(new[]{-1,1,2,5,5,9,11,11,16,21});

}

Narzędzia do wykonania tej pracy są

Moduł porównujący, który przyjmuje lambdę

public class Comparer<T> : IComparer<T>
{
    private readonly Func<T, T, int> _comparer;

    public Comparer(Func<T, T, int> comparer)
    {
        if (comparer == null)
            throw new ArgumentNullException("comparer");
        _comparer = comparer;
    }

    public int Compare(T x, T y)
    {
        return _comparer(x, y);
    }
}

Struktura tagowania

public struct Indexed<T>
{
    public int Index { get; private set; }
    public T Value { get; private set; }
    public Indexed(int index, T value) : this()
    {
        Index = index;
        Value = value;
    }

    public override string ToString()
    {
        return "(Indexed: " + Index + ", " + Value.ToString () + " )";
    }
}

public class Indexed
{
    public static Indexed<T> Create<T>(int indexed, T value)
    {
        return new Indexed<T>(indexed, value);
    }
}
bradgonesurfing
źródło
Zobacz moją drugą odpowiedź, aby uzyskać pełne zestawienie powyższych koncepcji w niestandardowej klasie ICollection
bradgonesurfing
0

W ten sposób rozwiązałem problem. Ma to być bezpieczne dla wątków, ale możesz po prostu usunąć je, lockjeśli tego nie potrzebujesz. Należy również zauważyć, że dowolny Insertindeks w indeksie nie jest obsługiwany, ponieważ może to naruszyć warunek sortowania.

public class ConcurrentOrderedList<Titem, Tsort> : ICollection<Titem>
{
    private object _lock = new object();
    private SortedDictionary<Tsort, List<Titem>> _internalLists;
    Func<Titem, Tsort> _getSortValue;
    
    public ConcurrentOrderedList(Func<Titem,Tsort> getSortValue)
    {
        _getSortValue = getSortValue;
        _internalLists = new SortedDictionary<Tsort, List<Titem>>();            
    }

    public int Count { get; private set; }

    public bool IsReadOnly => false;

    public void Add(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
            {
                values = new List<Titem>();
                _internalLists.Add(sortVal, values);
            }
            values.Add(item);
            Count++;
        }            
    }

    public bool Remove(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
                return false;

            var removed = values.Remove(item);
            if (removed)
                Count--;
            return removed;
        }
    }

    public void Clear()
    {
        lock (_lock)
        {
            _internalLists.Clear();
        }
    }

    public bool Contains(Titem item)
    {
        lock (_lock)
        {
            List<Titem> values;
            Tsort sortVal = _getSortValue(item);
            if (!_internalLists.TryGetValue(sortVal, out values))
                return false;
            return values.Contains(item);
        }
    }

    public void CopyTo(Titem[] array, int arrayIndex)
    {
        int i = arrayIndex;
        lock (_lock)
        {
            foreach (var list in _internalLists.Values)
            {
                list.CopyTo(array, i);
                i += list.Count;
            }
        }
    }

    public IEnumerator<Titem> GetEnumerator()
    {
        foreach (var list in _internalLists.Values)
        {
            foreach (var item in list)
                yield return item;
        }
    }

    public int IndexOf(Titem item)
    {
        int i = 0;
        var sortVal = _getSortValue(item);
        lock (_lock)
        {               
            foreach (var list in _internalLists)
            {
                if (object.Equals(list.Key, sortVal))
                {
                    int intIndex = list.Value.IndexOf(item);
                    if (intIndex == -1)
                        return -1;
                    return i + intIndex;
                }
                i += list.Value.Count;
            }
            return -1;
        }           
    }

    public void Insert(int index, Titem item)
    {
        throw new NotSupportedException();
    }

    // Note this method is indeterminate if there are multiple
    // items in the same sort position!
    public void RemoveAt(int index)
    {
        int i = 0;
        lock (_lock)
        {
            foreach (var list in _internalLists.Values)
            {
                if (i + list.Count < index)
                {
                    i += list.Count;
                    continue;
                }
                else
                {
                    list.RemoveAt(index - i);
                    return;
                }
            }
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
Peter Moore
źródło
-1

Utwórz klasę i wyszukaj listę:

Public Class SortingAlgorithm
{
    public int ID {get; set;}
    public string name {get; set;}
    public string address1 {get; set;}
    public string city {get; set;}
    public string state {get; set;}
    public int age {get; set;}
}

//declare a sorting algorithm list
List<SortingAlgorithm> sortAlg = new List<SortingAlgorithm>();

//Add multiple values to the list
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});
sortAlg.Add( new SortingAlgorithm() {ID = ID, name = name, address1 = address1, city = city, state = state, age = age});

//query and order by the list
  var sortedlist = (from s in sortAlg
                    select new { s.ID, s.name, s.address1, s.city, s.state, s.age })
                                                     .OrderBy(r => r.ID)
                                                     .ThenBy(r=> r.name)
                                                     .ThenBy(r=> r.city)
                                                     .ThenBy(r=>r.state)
                                                     .ThenBy(r=>r.age);
Satty FL
źródło
-1

Oto moje podejście do tego. Uważaj, tutaj mogą być smoki, C # wciąż jest dla mnie całkiem nowy.

  • Dozwolone są zduplikowane klucze, wartości są przechowywane na liście
  • Użyłem go jako sortowanej kolejki, stąd nazwy i metody

Stosowanie:

SortedQueue<MyClass> queue = new SortedQueue<MyClass>();
// new list on key "0" is created and item added
queue.Enqueue(0, first);
// new list on key "1" is created and item added
queue.Enqueue(1, second);
// items is added into list on key "0"
queue.Enqueue(0, third);
// takes the first item from list with smallest key
MyClass myClass = queue.Dequeue();
class SortedQueue<T> {
  public int Count;
  public SortedList<int, List<T>> Queue;

  public SortedQueue() {
    Count = 0;
    Queue = new SortedList<int, List<T>>();
  }

  public void Enqueue(int key, T value) {
    List<T> values;
    if (!Queue.TryGetValue(key, out values)){
      values = new List<T>();
      Queue.Add(key, values);
      Count += 1;
    }
    values.Add(value);
  }

  public T Dequeue() {
    if (Queue.Count > 0) {
      List<T> smallest = Queue.Values[0];
      if (smallest.Count > 0) {
        T item = smallest[0];
        smallest.Remove(item);
        return item;
      } else {
        Queue.RemoveAt(0);
        Count -= 1;
        return Dequeue();
      }
    }
    return default(T);
  }
}
Solo
źródło
W QueueBCL istnieje już klasa , która reprezentuje zbiór elementów pierwszy na wejściu, pierwszy na wyjściu. Semantyka twojej klasy jest inna. Twoja klasa ma początek (gdzie elementy są usuwane z kolejki), ale nie ma końca (element można wstawić w dowolnym miejscu). Więc Enqueuemetoda w twojej klasie jest bez znaczenia IMHO.
Theodor Zoulias
@TheodorZoulias Tak, nazewnictwo jest tu trochę gówniane, ale nie sądzę, żeby zasługiwało na negatywną opinię, ma to, czego potrzebuje OP i to tylko kwestia zmiany nazwy i ponownego wdrożenia metod wejścia / wyjścia. Dlaczego tak się nazywa? Potrzebowałem struktury, którą mogę opróżnić od początku w pętli while i dodać nowe elementy w oparciu o wartość priorytetu. Więc PriorityQueuebyłaby bardziej odpowiednia nazwa.
Solo
OP chce sortować kolekcję, która umożliwia zduplikowane klucze. Twoja klasa nie jest zbiorem , ponieważ nie można jej wyliczyć. Nie podoba mi się też korzystanie z pól publicznych. Nie odbieraj głosów przeciw do siebie. Możesz naprawić szkody reputacji wynikające z 5 głosów przeciw za pomocą jednego głosu za ( -2 * 5 == +10), więc to nic wielkiego. :-)
Theodor Zoulias