Domanda

Ci sono qualsiasi dizionario classi .NET libreria di classi base che permettono di duplicare le chiavi per essere utilizzato?L'unica soluzione che ho trovato è quello di creare, per esempio, una classe come:

Dictionary<string, List<object>>

Ma questo è abbastanza irritante per utilizzare effettivamente.In Java, credo che un MultiMap esegue questa operazione, ma non riesce a trovare un ingresso analogico .NET.

È stato utile?

Soluzione

Se si utilizza .NET 3.5, utilizzare Lookup classe.

EDIT: generalmente si crea un Enumerable.ToLookup utilizzando <=> . Ciò presume che non sia necessario modificarlo in seguito, ma in genere trovo che sia abbastanza buono.

Se quel non funziona per te, non penso che ci sia nulla nel framework che possa aiutare - e usare il dizionario è buono come si arriva :(

Altri suggerimenti

La classe List in realtà funziona abbastanza bene per le raccolte chiave / valore contenenti duplicati in cui si desidera scorrere la raccolta. Esempio:

List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();

// add some values to the collection here

for (int i = 0;  i < list.Count;  i++)
{
    Print(list[i].Key, list[i].Value);
}

Ecco un modo per farlo con List < & KeyValuePair lt; stringa, stringa > gt &;

public class ListWithDuplicates : List<KeyValuePair<string, string>>
{
    public void Add(string key, string value)
    {
        var element = new KeyValuePair<string, string>(key, value);
        this.Add(element);
    }
}

var list = new ListWithDuplicates();
list.Add("k1", "v1");
list.Add("k1", "v2");
list.Add("k1", "v3");

foreach(var item in list)
{
    string x = string.format("{0}={1}, ", item.Key, item.Value);
}

Output k1 = v1, k1 = v2, k1 = v3

Se si utilizza stringhe come le chiavi e i valori, è possibile utilizzare Sistema.Collezioni.Specializzato.NameValueCollection, che ritorna un array di valori di stringa tramite il GetValues(string key) metodo.

Mi sono appena imbattuto nella PowerCollections che include, tra le altre cose, una classe chiamata MultiDictionary. Questo avvolge perfettamente questo tipo di funzionalità.

Nota molto importante sull'uso della Ricerca:

Puoi creare un'istanza di Lookup(TKey, TElement) chiamando ToLookup su un oggetto che implementa IEnumerable(T)

Non esiste un costruttore pubblico per creare una nuova istanza di un <=>. Inoltre, <=> gli oggetti sono immutabili, ovvero non è possibile aggiungere o rimuovere elementi o chiavi da un <=> oggetto dopo che è stato creato.

(da MSDN)

Penso che questo sarebbe un punto fermo per la maggior parte degli usi.

Penso che qualcosa come List<KeyValuePair<object, object>> farebbe il lavoro.

Se si utilizza > = .NET 4, è possibile utilizzare Tuple Class:

// declaration
var list = new List<Tuple<string, List<object>>>();

// to add an item to the list
var item = Tuple<string, List<object>>("key", new List<object>);
list.Add(item);

// to iterate
foreach(var i in list)
{
    Console.WriteLine(i.Item1.ToString());
}

Dai un'occhiata a C5's HashBag classe.

È abbastanza facile " tirare il proprio " versione di un dizionario che consente " chiave duplicata " inserimenti. Ecco una semplice implementazione approssimativa. Potresti prendere in considerazione l'aggiunta del supporto per la maggior parte (se non tutti) su IDictionary<T>.

public class MultiMap<TKey,TValue>
{
    private readonly Dictionary<TKey,IList<TValue>> storage;

    public MultiMap()
    {
        storage = new Dictionary<TKey,IList<TValue>>();
    }

    public void Add(TKey key, TValue value)
    {
        if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
        storage[key].Add(value);
    }

    public IEnumerable<TKey> Keys
    {
        get { return storage.Keys; }
    }

    public bool ContainsKey(TKey key)
    {
        return storage.ContainsKey(key);
    }

    public IList<TValue> this[TKey key]
    {
        get
        {
            if (!storage.ContainsKey(key))
                throw new KeyNotFoundException(
                    string.Format(
                        "The given key {0} was not found in the collection.", key));
            return storage[key];
        }
    }
}

Un rapido esempio su come usarlo:

const string key = "supported_encodings";
var map = new MultiMap<string,Encoding>();
map.Add(key, Encoding.ASCII);
map.Add(key, Encoding.UTF8);
map.Add(key, Encoding.Unicode);

foreach (var existingKey in map.Keys)
{
    var values = map[existingKey];
    Console.WriteLine(string.Join(",", values));
}

In risposta alla domanda originale. Qualcosa come Dictionary<string, List<object>> è implementato in una classe chiamata MultiMap in The Code Project.

Puoi trovare maggiori informazioni al seguente link: http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

NameValueCollection supporta più valori di stringa in un'unica chiave (che è anche una stringa), ma è l'unico esempio di cui sono a conoscenza.

Tendo a creare costrutti simili a quello del tuo esempio quando incontro situazioni in cui ho bisogno di quel tipo di funzionalità.

Quando si utilizza l'opzione List<KeyValuePair<string, object>>, è possibile utilizzare LINQ per eseguire la ricerca:

List<KeyValuePair<string, object>> myList = new List<KeyValuePair<string, object>>();
//fill it here
var q = from a in myList Where a.Key.Equals("somevalue") Select a.Value
if(q.Count() > 0){ //you've got your value }

Il modo in cui uso è solo un

Dictionary<string, List<string>>

In questo modo hai una sola chiave che contiene un elenco di stringhe.

Esempio:

List<string> value = new List<string>();
if (dictionary.Contains(key)) {
     value = dictionary[key];
}
value.Add(newValue);

Intendi congruente e non un vero duplicato? Altrimenti una tabella hash non sarebbe in grado di funzionare.

Congruente significa che due chiavi separate possono eseguire l'hash sul valore equivalente, ma le chiavi non sono uguali.

Ad esempio: supponiamo che la funzione hash della vostra hashtable fosse solo hashval = key mod 3. Sia 1 che 4 sono mappati su 1, ma hanno valori diversi. È qui che entra in gioco la tua idea di un elenco.

Quando è necessario cercare 1, quel valore viene hash a 1, l'elenco viene attraversato fino a quando non viene trovata la chiave = 1.

Se si consentisse l'inserimento di chiavi duplicate, non si sarebbe in grado di differenziare quali chiavi corrispondono a quali valori.

Mi sono imbattuto in questo post alla ricerca della stessa risposta e non ho trovato nessuno, quindi ho preparato una soluzione di esempio bare-bones usando un elenco di dizionari, ignorando l'operatore [] per aggiungere un nuovo dizionario all'elenco quando tutti altri hanno una determinata chiave (set) e restituiscono un elenco di valori (get).
È brutto e inefficiente, ottiene / imposta SOLO dalla chiave e restituisce sempre un elenco, ma funziona:

 class DKD {
        List<Dictionary<string, string>> dictionaries;
        public DKD(){
            dictionaries = new List<Dictionary<string, string>>();}
        public object this[string key]{
             get{
                string temp;
                List<string> valueList = new List<string>();
                for (int i = 0; i < dictionaries.Count; i++){
                    dictionaries[i].TryGetValue(key, out temp);
                    if (temp == key){
                        valueList.Add(temp);}}
                return valueList;}
            set{
                for (int i = 0; i < dictionaries.Count; i++){
                    if (dictionaries[i].ContainsKey(key)){
                        continue;}
                    else{
                        dictionaries[i].Add(key,(string) value);
                        return;}}
                dictionaries.Add(new Dictionary<string, string>());
                dictionaries.Last()[key] =(string)value;
            }
        }
    }

Ho modificato la risposta di @Hector Correa in un'estensione con tipi generici e ho anche aggiunto un TryGetValue personalizzato.

  public static class ListWithDuplicateExtensions
  {
    public static void Add<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
    {
      var element = new KeyValuePair<TKey, TValue>(key, value);
      collection.Add(element);
    }

    public static int TryGetValue<TKey, TValue>(this List<KeyValuePair<TKey, TValue>> collection, TKey key, out IEnumerable<TValue> values)
    {
      values = collection.Where(pair => pair.Key.Equals(key)).Select(pair => pair.Value);
      return values.Count();
    }
  }

Questo è un modo di rimorchio Dizionario simultaneo penso che questo ti aiuterà:

public class HashMapDictionary<T1, T2> : System.Collections.IEnumerable
{
    private System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>> _keyValue = new System.Collections.Concurrent.ConcurrentDictionary<T1, List<T2>>();
    private System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>> _valueKey = new System.Collections.Concurrent.ConcurrentDictionary<T2, List<T1>>();

    public ICollection<T1> Keys
    {
        get
        {
            return _keyValue.Keys;
        }
    }

    public ICollection<T2> Values
    {
        get
        {
            return _valueKey.Keys;
        }
    }

    public int Count
    {
        get
        {
            return _keyValue.Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return false;
        }
    }

    public List<T2> this[T1 index]
    {
        get { return _keyValue[index]; }
        set { _keyValue[index] = value; }
    }

    public List<T1> this[T2 index]
    {
        get { return _valueKey[index]; }
        set { _valueKey[index] = value; }
    }

    public void Add(T1 key, T2 value)
    {
        lock (this)
        {
            if (!_keyValue.TryGetValue(key, out List<T2> result))
                _keyValue.TryAdd(key, new List<T2>() { value });
            else if (!result.Contains(value))
                result.Add(value);

            if (!_valueKey.TryGetValue(value, out List<T1> result2))
                _valueKey.TryAdd(value, new List<T1>() { key });
            else if (!result2.Contains(key))
                result2.Add(key);
        }
    }

    public bool TryGetValues(T1 key, out List<T2> value)
    {
        return _keyValue.TryGetValue(key, out value);
    }

    public bool TryGetKeys(T2 value, out List<T1> key)
    {
        return _valueKey.TryGetValue(value, out key);
    }

    public bool ContainsKey(T1 key)
    {
        return _keyValue.ContainsKey(key);
    }

    public bool ContainsValue(T2 value)
    {
        return _valueKey.ContainsKey(value);
    }

    public void Remove(T1 key)
    {
        lock (this)
        {
            if (_keyValue.TryRemove(key, out List<T2> values))
            {
                foreach (var item in values)
                {
                    var remove2 = _valueKey.TryRemove(item, out List<T1> keys);
                }
            }
        }
    }

    public void Remove(T2 value)
    {
        lock (this)
        {
            if (_valueKey.TryRemove(value, out List<T1> keys))
            {
                foreach (var item in keys)
                {
                    var remove2 = _keyValue.TryRemove(item, out List<T2> values);
                }
            }
        }
    }

    public void Clear()
    {
        _keyValue.Clear();
        _valueKey.Clear();
    }

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

Esempi:

public class TestA
{
    public int MyProperty { get; set; }
}

public class TestB
{
    public int MyProperty { get; set; }
}

            HashMapDictionary<TestA, TestB> hashMapDictionary = new HashMapDictionary<TestA, TestB>();

            var a = new TestA() { MyProperty = 9999 };
            var b = new TestB() { MyProperty = 60 };
            var b2 = new TestB() { MyProperty = 5 };
            hashMapDictionary.Add(a, b);
            hashMapDictionary.Add(a, b2);
            hashMapDictionary.TryGetValues(a, out List<TestB> result);
            foreach (var item in result)
            {
                //do something
            }

Uso questa semplice classe:

public class ListMap<T,V> : List<KeyValuePair<T, V>>
{
    public void Add(T key, V value) {
        Add(new KeyValuePair<T, V>(key, value));
    }

    public List<V> Get(T key) {
        return FindAll(p => p.Key.Equals(key)).ConvertAll(p=> p.Value);
    }
}

utilizzo:

var fruits = new ListMap<int, string>();
fruits.Add(1, "apple");
fruits.Add(1, "orange");
var c = fruits.Get(1).Count; //c = 2;

U può definire un metodo per costruire una chiave di stringa composta ogni volta che vuoi usare il dizionario devi usare questo metodo per costruire la tua chiave ad esempio:

private string keyBuilder(int key1, int key2)
{
    return string.Format("{0}/{1}", key1, key2);
}

per l'utilizzo:

myDict.ContainsKey(keyBuilder(key1, key2))

Duplicato chiavi di pausa per intero il contratto di Dizionario.In un dizionario ogni chiave è unica e associato a un singolo valore.Se si desidera collegare un oggetto a un numero arbitrario di oggetti aggiuntivi, la cosa migliore potrebbe essere qualcosa di simile a un set di dati (nel linguaggio comune una tabella).Mettere le chiavi in una colonna e i tuoi valori negli altri.Questo è significativamente più lento di un dizionario, ma questo è il compromesso per perdere la capacità di hash della chiave di oggetti.

Anche questo è possibile:

Dictionary<string, string[]> previousAnswers = null;

In questo modo, possiamo avere chiavi univoche. Spero che questo funzioni per te.

Puoi aggiungere le stesse chiavi con casi diversi come:

key1
key1
KEY1
key1
key1
key1

So che è una risposta fittizia, ma ha funzionato per me.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top