source

에 중복된 키가 있습니다.NET 사전?

ittop 2023. 5. 2. 22:58
반응형

에 중복된 키가 있습니다.NET 사전?

에 사전 클래스가 있습니까?중복 키를 사용할 수 있는 NET 기본 클래스 라이브러리를 선택하십시오.내가 찾은 유일한 해결책은 예를 들어 다음과 같은 클래스를 만드는 것입니다.

Dictionary<string, List<object>>

하지만 이것은 실제로 사용하기에는 꽤 짜증나는 일입니다.Java에서 MultiMap은 이를 수행하지만 에서 아날로그를 찾을 수 없습니다.그물.

를 사용하는 경우.NET 3.5, 클래스를 사용합니다.

으로 집편: .Lookup…을 사용하여이는 나중에 변경할 필요가 없다고 가정하지만 일반적으로 충분하다고 생각합니다.

만약 그것이 당신에게 효과가 없다면, 저는 프레임워크에 도움이 될 만한 것이 없다고 생각합니다 - 그리고 사전을 사용하는 것은 얻는 것만큼 좋습니다 :(

목록 클래스는 실제로 컬렉션 위에서 반복할 중복 항목이 포함된 키/값 컬렉션에 매우 적합합니다.예:

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

List< KeyValuePair< string, string >로 이를 수행하는 한 가지 방법이 있습니다.

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

출력 k1=v1, k1=v2, k1=v3

문자열을 키와 값으로 모두 사용하는 경우 시스템을 사용할 수 있습니다.컬렉션.전문화된.NameValueCollection - GetValues(문자열 키) 메서드를 통해 문자열 값 배열을 반환합니다.

저는 방금 PowerCollections 라이브러리를 발견했습니다. 여기에는 MultiDictionary라는 클래스가 포함되어 있습니다.이것은 이러한 유형의 기능을 깔끔하게 포장합니다.

Lookup 사용과 관련하여 매우 중요한 참고 사항:

의인스를생수있다니의 수 .Lookup(TKey, TElement)화로로 ToLookup을 에.IEnumerable(T)

새인스를만공없생다습니성의 새 를 만들 .Lookup(TKey, TElement).또한.Lookup(TKey, TElement)즉, 할 수 .Lookup(TKey, TElement)개체를 만든 후 개체를 클릭합니다.

(MSDN에서)

저는 이것이 대부분의 용도의 쇼 스토퍼가 될 것이라고 생각합니다.

새로운 C#(7.0의 C#인 것으로 생각됨)을 사용하면 다음과 같은 작업을 수행할 수 있습니다.

var duplicatedDictionaryExample = new List<(string Key, string Value)> { ("", "") ... }

그리고 당신은 그것을 표준 목록으로 사용하고 있지만, 당신이 원하는 대로 이름을 붙인 두 개의 값을 가지고 있습니다.

foreach(var entry in duplicatedDictionaryExample)
{ 
    // do something with the values
    entry.Key;
    entry.Value;
}

제 생각에는 그런 것 같아요.List<KeyValuePair<object, object>>그 일을 할 것입니다.

> .> = 를 하는 경우.를 사용하면 NET 4를 사용자는 NET 4를 사용할 수 .Tuple 다음과 같습니다.

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

"키 복제" 항목을 허용하는 사전의 "사용자 고유의" 버전을 "롤링"하는 것은 매우 쉽습니다.다음은 대략적인 간단한 구현입니다.으로 대부분하는 것을 해 보는 .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];
        }
    }
}

사용 방법에 대한 간단한 예:

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

원래 질문에 대한 답변입니다. 같은 거.Dictionary<string, List<object>>는 라는클구현다니됩서라는 됩니다.MultiMapCode Project.

아래 링크에서 더 많은 정보를 찾을 수 있습니다. http://www.codeproject.com/KB/cs/MultiKeyDictionary.aspx

NameValueCollection은 하나의 키(또한 문자열) 아래에서 여러 문자열 값을 지원하지만, 이는 내가 알고 있는 유일한 예입니다.

저는 그런 기능이 필요한 상황에 부딪힐 때 당신의 예와 유사한 구조를 만드는 경향이 있습니다.

List<KeyValuePair<string, object>>할 수 있습니다. LINQ 사용검수있습니다할색여하.

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 }

당신은 실제 복제품이 아니라 일치한다는 것을 의미합니까?그렇지 않으면 해시 테이블이 작동하지 않습니다.

일치는 두 개의 개별 키가 동등한 값으로 해시될 수 있지만 키가 같지 않다는 것을 의미합니다.

예를 들어, 해시 테이블의 해시 함수가 단지 hashval = key mod 3이었다고 가정합니다.1과 4는 모두 1에 매핑되지만 다른 값입니다.이것이 목록에 대한 당신의 생각이 작용하는 부분입니다.

1을 조회해야 하는 경우 해당 값이 1로 해시되면 키 = 1이 발견될 때까지 목록이 순회됩니다.

중복 키를 삽입하도록 허용한 경우 어떤 키가 어떤 값에 매핑되는지 구분할 수 없습니다.

제가 사용하는 방법은 그냥

Dictionary<string, List<string>>

이렇게 하면 문자열 목록이 들어 있는 단일 키를 가질 수 있습니다.

예:

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

다음과 같은 사전 래퍼를 만들 수 있습니다. 보너스로 null 값을 키로 지원합니다.

/// <summary>
/// Dictionary which supports duplicates and null entries
/// </summary>
/// <typeparam name="TKey">Type of key</typeparam>
/// <typeparam name="TValue">Type of items</typeparam>
public class OpenDictionary<TKey, TValue>
{
    private readonly Lazy<List<TValue>> _nullStorage = new Lazy<List<TValue>>(
        () => new List<TValue>());

    private readonly Dictionary<TKey, List<TValue>> _innerDictionary =
        new Dictionary<TKey, List<TValue>>();

    /// <summary>
    /// Get all entries
    /// </summary>
    public IEnumerable<TValue> Values =>
        _innerDictionary.Values
            .SelectMany(x => x)
            .Concat(_nullStorage.Value);

    /// <summary>
    /// Add an item
    /// </summary>
    public OpenDictionary<TKey, TValue> Add(TKey key, TValue item)
    {
        if (ReferenceEquals(key, null))
            _nullStorage.Value.Add(item);
        else
        {
            if (!_innerDictionary.ContainsKey(key))
                _innerDictionary.Add(key, new List<TValue>());

            _innerDictionary[key].Add(item);
        }

        return this;
    }

    /// <summary>
    /// Remove an entry by key
    /// </summary>
    public OpenDictionary<TKey, TValue> RemoveEntryByKey(TKey key, TValue entry)
    {
        if (ReferenceEquals(key, null))
        {
            int targetIdx = _nullStorage.Value.FindIndex(x => x.Equals(entry));
            if (targetIdx < 0)
                return this;

            _nullStorage.Value.RemoveAt(targetIdx);
        }
        else
        {
            if (!_innerDictionary.ContainsKey(key))
                return this;

            List<TValue> targetChain = _innerDictionary[key];
            if (targetChain.Count == 0)
                return this;

            int targetIdx = targetChain.FindIndex(x => x.Equals(entry));
            if (targetIdx < 0)
                return this;

            targetChain.RemoveAt(targetIdx);
        }

        return this;
    }

    /// <summary>
    /// Remove all entries by key
    /// </summary>
    public OpenDictionary<TKey, TValue> RemoveAllEntriesByKey(TKey key)
    {
        if (ReferenceEquals(key, null))
        {
            if (_nullStorage.IsValueCreated)
                _nullStorage.Value.Clear();
        }       
        else
        {
            if (_innerDictionary.ContainsKey(key))
                _innerDictionary[key].Clear();
        }

        return this;
    }

    /// <summary>
    /// Try get entries by key
    /// </summary>
    public bool TryGetEntries(TKey key, out IReadOnlyList<TValue> entries)
    {
        entries = null;

        if (ReferenceEquals(key, null))
        {
            if (_nullStorage.IsValueCreated)
            {
                entries = _nullStorage.Value;
                return true;
            }
            else return false;
        }
        else
        {
            if (_innerDictionary.ContainsKey(key))
            {
                entries = _innerDictionary[key];
                return true;
            }
            else return false;
        }
    }
}

사용 예:

var dictionary = new OpenDictionary<string, int>();
dictionary.Add("1", 1); 
// The next line won't throw an exception; 
dictionary.Add("1", 2);

dictionary.TryGetEntries("1", out List<int> result); 
// result is { 1, 2 }

dictionary.Add(null, 42);
dictionary.Add(null, 24);
dictionary.TryGetEntries(null, out List<int> result); 
// result is { 42, 24 }

같은 답을 찾다가 우연히 이 게시물을 발견하지 못했기 때문에 사전 목록을 사용하여 맨본 예제 솔루션을 구성하고, 다른 모든 사용자가 지정된 키(set)를 가지고 있을 때 목록에 새 사전을 추가하도록 [] 연산자를 재정의하고 값 목록(get)을 반환했습니다.
보기 흉하고 비효율적이며 키별로 가져오기/설정만 하고 항상 목록을 반환하지만 작동합니다.

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

나는 @Hector Correa의 답변을 제네릭 형식의 확장자로 변경하고 사용자 정의 TryGetValue를 추가했습니다.

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

이것은 당신에게 도움이 될 것이라고 생각하는 동시 사전입니다.

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

예:

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
            }

저는 이 간단한 수업을 사용합니다.

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

용도:

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

사전을 사용하여 복합 문자열 키를 작성할 방법을 정의할 수 있습니다. 이 방법을 사용하여 키를 작성해야 합니다. 예를 들어 다음과 같습니다.

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

사용 용도:

myDict.ContainsKey(keyBuilder(key1, key2))

중복 키는 사전의 전체 계약을 위반합니다.사전에서 각 키는 고유하며 단일 값에 매핑됩니다.개체를 임의 개수의 추가 개체에 연결하려면 DataSet(일반적으로 테이블)과 유사한 것이 가장 좋습니다.키를 한 열에 넣고 값을 다른 열에 넣습니다.이것은 사전에 비해 상당히 느리지만, 핵심 개체를 해시하는 기능을 상실한 것에 대한 트레이드오프입니다.

또한 다음과 같은 작업이 가능합니다.

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

이런 식으로, 우리는 고유한 열쇠를 가질 수 있습니다.이것이 당신에게 효과가 있기를 바랍니다.

다음과 같은 다른 대소문자를 사용하여 동일한 키를 추가할 수 있습니다.

키1
키 1
키1
KeY1
kEy1
키 Y1

바보 같은 대답인 건 알지만, 저를 위해 일했습니다.

언급URL : https://stackoverflow.com/questions/146204/duplicate-keys-in-net-dictionaries

반응형