C # Yinelenen anahtarlara izin veren sıralanabilir koleksiyon


97

Raporda çeşitli nesnelerin görüneceği bir sırayı ayarlamak için bir program yazıyorum. Sıra, Excel elektronik tablosundaki Y konumudur (hücre).

Kodun bir demo kısmı aşağıdadır. Başarmak istediğim şey, birden fazla nesne eklememe izin verecek bir koleksiyona sahip olmak ve sıraya göre sıralı bir koleksiyon elde edebilmek.

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

Buna SortedListizin vermeyeceğini biliyorum ve alternatif arıyordum. Yinelenenleri ortadan kaldırmak istemiyorum ve zaten denedim List<KeyValuePair<int, object>>.

Teşekkürler.


1
Koleksiyonun , ilk üye listesi verildikten sonra ekleme / çıkarma işlemlerini desteklemesi gerekiyor mu?
Ani

2
Denediğinde ne işe yaramadı List?
diceguyd30

Sadece sıralamak ve nesneyi almak istemiyorum. Ama bunun yerine sıralı listenin tamamını almak istiyorum. Bu nedenle, aşağıdaki örnekte, hem Header nesneleri var olmalı ve sırayla birbirinin altında olmalıdır. XPos = 2 ile başka bir Başlık nesnesi eklersem, listede 3 nesneye sahip olmalıyım, 2 nesne
XPos

Sadece bir not: Bu tür bir durumla karşılaştığımda, genel Listenin, bulunamayan öğeler için az bilinen BinarySearch davranışı ile birlikte harikalar yarattığını görüyorum .
J Trana

Yanıtlar:


81

Kendi IComparer'ınızı kullanın!

Diğer bazı cevaplarda da belirtildiği gibi, kendi karşılaştırma sınıfınızı kullanmalısınız. Bunun için, IComparable uygulayan herhangi bir şeyle çalışan genel bir IComparer sınıfı kullanıyorum:

/// <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
}

Yeni bir SortedList, SortedDictionary vb. Örnekler oluştururken kullanacaksınız:

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

Burada int yinelenebilecek anahtardır.


43
Ancak ondan herhangi bir anahtarı kaldıramazsınız.
Shashwat

11
Evet bu doğru, Shachwat! Kaldır (anahtar) veya IndexOfKey (anahtar) kullanamazsınız çünkü karşılaştırıcı anahtar eşitliğini belirtmek için asla 0 döndürmez. Ancak, dizinlerine sahipseniz öğeleri silmek için RemoveAt (dizin) yapabilirsiniz.
Knasterbax

1
Ben de aynı problemle karşılaştım, kullandım SortedDictionary. Çıkarmaya da izin verir.
Shashwat

10
Bu şekilde karşılaştırıcınızın refleksivitesini kırdığınızı unutmayın . BCL'deki şeyleri kırabilir (ve kıracaktır).
ghord

2
bu aslında düzeni sağlamak için -1 döndürmeli
M.kazem Akhgary

16

List <> 'i güvenle kullanabilirsiniz. List, aşırı yüklenmesi IComparer'ı kabul eden bir Sort yöntemine sahiptir. Olarak kendi sıralayıcı sınıfınızı oluşturabilirsiniz. İşte bir örnek:

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

1
Sadece sıralamak ve nesneyi almak istemiyorum. Ama bunun yerine sıralı listenin tamamını almak istiyorum. Bu nedenle, aşağıdaki örnekte, hem Header nesneleri var olmalı ve sırayla birbirinin altında olmalıdır. XPos = 2 ile başka bir Header nesnesi eklersem, listede 3 nesneye sahip olmalıyım, 2 nesne
XPos

1
Pekala, listeye bir öğe eklendiğinde, sıralamaya göre doğru konuma yerleştirilmesi gerektiğini söylüyorsunuz. Lütfen yanlışsa düzeltin. Bir bakayım, kısa sürede geri döneceğim
Dipti Mehta

List <T> .Sort'un koleksiyon boyutuna bağlı olarak birden çok sıralama algoritması kullandığını ve hepsinin kararlı sıralama olmadığını unutmayın. Bu nedenle, koleksiyona eşdeğerle karşılaştırılan nesneler eklendikleri sırada görünmeyebilir.
sessiz ton

bir sözlüğe azaltma işlevlerini uygulayarak aşırı miktarda KeyValuePairs oluşturmayı bırakabilmek için bu seçeneği kullandım
Chris Marisic

10

Ben aşağıdakileri kullanıyorum:

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

}

Test durumum:

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

Çıktı:

1:cab
1:coconut
1:car
1:cat
2:door
2:dog
3:elephant

Tuple, .NET'in tüm sürümlerinde mevcut değildir, ancak KeyValuePair <K, V> ile değiştirilebilir
Reuben

7

Sorun, veri yapısı tasarımının gereksinimleri karşılamamasıdır: Aynı XPolar için birkaç Başlığın saklanması gerekir. Bu nedenle, SortedList<XPos, value>bir değerine sahip olmamalıdır Header, ama değeri List<Header>. Bu basit ve küçük bir değişikliktir, ancak tüm sorunları çözer ve diğer önerilen çözümler gibi yeni sorunlar yaratmaktan kaçınır (aşağıdaki açıklamaya bakın):

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

Rastgele bir sayı eklemek veya aynı değere sahip 2 XPo gibi davranmak gibi "komik" bir anahtar eklemenin birçok başka soruna yol açtığını lütfen unutmayın. Örneğin, belirli bir Başlığı kaldırmak zor hatta imkansız hale gelir.

Ayrıca, List<Header>her birinden yalnızca birkaçının sıralanması gerekiyorsa sıralama performansının çok daha iyi olduğunu unutmayın Header. Örnek: 100 XPo varsa ve her birinde 100 başlık varsa, 100 yerine 10.000'inin Headersıralanması gerekir List<Header>.

Tabii ki, bu çözümün de bir dezavantajı var: Sadece 1 Başlığa sahip çok sayıda XPo varsa, birçok Listenin oluşturulması gerekir ki bu biraz ek yüktür.


Bu en basit çözümdür. Ayrıca SortedDictionary'yi kontrol edin, benzerdir, bazı durumlarda daha hızlıdır.
Hogan

Bu gerçekten iyi bir çözüm. Biri, bu işlevselliği bazı özel koleksiyon nesnelerine kolayca sarabilir ve oldukça yararlı olabilir. İyi fikir, Peter'ı paylaştığın için teşekkürler!
Adam P

5

En basit çözüm (yukarıdakilerin hepsine kıyasla): kullanın SortedSet<T>, bir IComparer<SortableKey>sınıfı kabul eder , ardından Karşılaştırma yöntemini şu şekilde uygulayın:

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

}

4
SortedSet <T> kullandım ve T'nin, diğer Alanlar aynı olsa bile her T'nin benzersiz olmasını sağlamak için her bir örneklemede artan kendi artan int kimliği vardı.
Skychan

3
Karşılaştırma için GetHashCode tehlikelidir. Beklenmedik yanlış kopyalara yol açabilir. Çoğu zaman işe yarayabilir, ama bunu asla ciddi bir şey için kullanmam.
Hogan

4

Yardımın için çok teşekkürler. Daha fazlasını ararken bu çözümü buldum. (Diğer soruda Stackoverflow.com'da mevcut)

İlk olarak, nesnelerimi sınıflar için (Üstbilgi, Altbilgi vb.)

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

Yani bu sınıfın nesneleri tutması gerekiyor ve her nesnenin PosX'i int Position olarak gidiyor

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

Sonunda aldığım şey sıralı "Sıra" listesidir.


2

Lookup<TKey, TElement>Yinelenen anahtarlara izin vermeyi denediniz mi http://msdn.microsoft.com/en-us/library/bb460184.aspx


Teşekkürler. Benim sorunum nesneleri yalnızca bir tür (adil değil Başlık), bu (Kenar çubuğu vb, diyelim Altbilgi'yi sağlar) değişebilir olmayacak ama her XPOS sahip olacaktır
Mayur Kotlikar

Ayrıca, Lookupinandığım bir kamu kurucusu da yok . Bunun iyi bir yolu var mı?
Jeff B

1
@JeffBridgman Linq'e güvenmeniz gerekecek. ToLookupHerhangi birini yapabilirsiniz IEnumerable<T>.
nawfal

8
Evet, yinelenen anahtarlara izin verir, ancak hiçbir şeyi sıralı tutmaz!
Roman Starkov

2

SortedList'i kullanabilir, değerinizi TKey için ve int (count) TValue için kullanabilirsiniz.

İşte bir örnek: Bir kelimenin harflerini sıralayan bir işlev.

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

    }

2

Bu koleksiyon sınıfı, kopyaları saklar ve kopya için sıralama düzeni ekler. İşin püf noktası, sabit bir sıralama düzeni sağlamak için eklendiklerinde öğeleri benzersiz bir değerle etiketlemektir. Sonra hepsini bir ICollection arabirimine sarıyoruz.

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

bir test sınıfı

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

}

Etiketleme yapısı

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

Lambda karşılaştırma yardımcısı

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

1

Sorun, anahtar olmayan bir şeyi anahtar olarak kullanmanızdır (çünkü birden çok kez meydana gelir).

Dolayısıyla, gerçek koordinatlarınız varsa Point, SortedList'inizin anahtarı olarak almalısınız .

Ya da List<List<Header>>, ilk liste dizininizin x konumunu ve iç liste dizininin y konumunu tanımladığı bir yerde (veya isterseniz tersi) bir yer yaratırsınız.


Bir Anahtar, birincil anahtar olmadığı sürece birden çok örneğe sahip olabilir. En azından aldığım Veritabanları dersinde bana söyledikleri buydu.
amalgamı

1
Bu cevap biraz kısadır, ancak sorunu doğru bir şekilde açıklar ve doğru çözümü sağlar, yani SortedList <int, List <Header>>. Bu, başlığı sıralı tutar ve aynı xPos'ta birçok başlığı saklayabilir. Bir kod örneği için cevabıma bakın. Doğru yönü gösterdiği için bu yanıtı eleştirdim. Faydalı olduğunu düşünüyorsanız lütfen artı 1 cevabımı.
Peter Huber

1

Bunun anahtarı (amaçlanan), IComparableeşitliği ve hashing'i koruyan, ancak eşit değilse asla 0 ile karşılaştırmayan bir -based sınıf oluşturmaktır . Bu yapılabilir ve birkaç bonusla oluşturulabilir - kararlı sıralama (yani, sıralanan listeye önce eklenen değerler konumlarını koruyacaktır) veToString() basitçe gerçek anahtar dizesi değerini döndürebilir.

İşte hile yapması gereken bir struct anahtarı:

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
    }
}

Bu güzel bir fikir. Konsepti özel bir ICollection içine sardım. Bkz. Stackoverflow.com/a/21625939/158285
bradgonesurfing

1

Sorunu bu şekilde çözdüm. İş parçacığı açısından güvenli olması amaçlanmıştır, ancak lockbuna ihtiyacınız yoksa basitçe s'leri kaldırabilirsiniz . Ayrıca Insert, bir dizinde keyfi olarak, sıralama koşulunu ihlal edebileceği için desteklenmediğini unutmayın .

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

0

Linq.Lookup harika ve hepsi, ancak hedefiniz basitçe "anahtarlar" üzerinde döngü oluştururken kopyalanmalarına izin vermekse , şu yapıyı kullanabilirsiniz:

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"),
};

O zaman yazabilirsiniz:

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

HTH


0

İşin püf noktası, nesnenizi benzersiz bir anahtarla büyütmektir. Başarılı olan aşağıdaki teste bakın. Puanlarımı X değerlerine göre sıralamak istiyorum. Karşılaştırma işlevimde çıplak bir Point2D kullanmak, aynı X değerine sahip noktaların elenmesine neden olur. Bu yüzden Point2D'yi Indexed adlı bir etiketleme sınıfına sarıyorum.

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

}

Bu işi yapacak araçlar

Lambda alan bir karşılaştırıcı

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

Bir etiketleme yapısı

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

Yukarıdaki kavramların özel bir ICollection sınıfına tam olarak sarılması için diğer cevabımı görün
bradgonesurfing

-1

Bir sınıf oluşturun ve listeyi sorgulayın:

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

-1

İşte benim bu konudaki görüşüm. Dikkat edin, burada ejderhalar olabilir, C # benim için hala oldukça yeni.

  • Yinelenen anahtarlara izin verilir, değerler bir listede saklanır
  • Sıralı bir kuyruk olarak kullandım, dolayısıyla isimler ve yöntemler

Kullanım:

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

QueueBCL'de zaten ilk giren ilk çıkar öğe koleksiyonunu temsil eden bir sınıf vardır. Sınıfınızın semantiği farklı. Sınıfınızın bir başlangıcı vardır (öğelerin sırasının kaldırıldığı) ancak sonu yoktur (öğe herhangi bir yere eklenebilir). Yani Enqueuesınıfınızdaki yöntem anlamsız IMHO.
Theodor Zoulias

@TheodorZoulias Yup, burada adlandırma biraz boktan bir şey ama bir eksi oyu hak ettiğini düşünmüyorum, OP'nin ihtiyacı olan şeylere sahip ve bu sadece girdi / çıktı yöntemlerini yeniden adlandırmak ve yeniden uygulamak meselesi. Neden böyle adlandırılıyor? Bir while döngüsünde baştan boşaltabileceğim ve öncelik değerine göre yeni öğeler ekleyebileceğim bir yapıya ihtiyacım vardı. Bu yüzden PriorityQueuedaha uygun bir isim olacaktır.
Solo

OP, yinelenen anahtarlara izin veren sıralanabilir bir koleksiyon ister. Sınıfınız numaralandırılamadığı için bir koleksiyon değildir. Ayrıca kamusal alanların kullanımını da sevmiyorum. Olumsuz oyları kişisel olarak almayın. Tek bir olumlu oyla ( -2 * 5 == +10) 5 olumsuz oyun itibar hasarını onarabilirsiniz , bu yüzden çok önemli değil. :-)
Theodor Zoulias
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.