IT

숫자 인덱스를 통해 Dictionary.Keys 키에 액세스

lottoking 2020. 6. 9. 07:49
반응형

숫자 인덱스를 통해 Dictionary.Keys 키에 액세스


키가 있는 Dictionary<string, int>곳을 사용하고 int있습니다.

이제 사전 내에 마지막으로 삽입 된 키에 액세스해야하지만 그 이름을 모릅니다. 명백한 시도 :

int LastCount = mydict[mydict.keys[mydict.keys.Count]];

Dictionary.Keys[] -indexer를 구현하지 않기 때문에 작동하지 않습니다.

비슷한 수업이 있는지 궁금합니다. 스택 사용에 대해 생각했지만 문자열 만 저장합니다. 이제 내 자신의 구조체를 만든 다음을 사용할 수 Stack<MyStruct>있지만 키에 [] -indexer를 구현하는 또 다른 대안, 사전이 있는지 궁금합니다.


@Falanwe가 주석에서 지적했듯이 이와 같은 작업은 올바르지 않습니다 .

int LastCount = mydict.Keys.ElementAt(mydict.Count -1);

당신은 사전에있는 키의 순서에 따라 달라집니다. 주문이 필요한 경우이 답변 에서 제안한대로 OrderedDictionary를 사용해야합니다 . 이 페이지의 다른 답변들도 흥미 롭습니다.


OrderedDictionary 를 사용할 수 있습니다 .

키 또는 인덱스로 액세스 할 수있는 키 / 값 쌍 모음을 나타냅니다.


사전은 해시 테이블이므로 삽입 순서를 모릅니다.

마지막으로 삽입 된 키를 알고 싶다면 LastKeyInserted 값을 포함하도록 사전을 확장하는 것이 좋습니다.

예 :

public MyDictionary<K, T> : IDictionary<K, T>
{
    private IDictionary<K, T> _InnerDictionary;

    public K LastInsertedKey { get; set; }

    public MyDictionary()
    {
        _InnerDictionary = new Dictionary<K, T>();
    }

    #region Implementation of IDictionary

    public void Add(KeyValuePair<K, T> item)
    {
        _InnerDictionary.Add(item);
        LastInsertedKey = item.Key;

    }

    public void Add(K key, T value)
    {
        _InnerDictionary.Add(key, value);
        LastInsertedKey = key;
    }

    .... rest of IDictionary methods

    #endregion

}

그러나 .Remove()이를 극복하기 위해 사용할 때 문제가 발생할 수 있으며 순서대로 정렬 된 키 목록을 유지해야합니다.


마지막 키 삽입 속성에 추가하기 위해 사전 클래스를 확장하지 않는 이유는 무엇입니까? 다음과 같은 것이 있습니까?

public class ExtendedDictionary : Dictionary<string, int>
{
    private int lastKeyInserted = -1;

    public int LastKeyInserted
    {
        get { return lastKeyInserted; }
        set { lastKeyInserted = value; }
    }

    public void AddNew(string s, int i)
    {
        lastKeyInserted = i;

        base.Add(s, i);
    }
}


당신은 항상 이것을 할 수 있습니다 :

string[] temp = new string[mydict.count];
mydict.Keys.CopyTo(temp, 0)
int LastCount = mydict[temp[mydict.count - 1]]

그러나 나는 그것을 추천하지 않을 것입니다. 마지막으로 삽입 된 키가 배열의 끝에있을 것이라는 보장은 없습니다. MSDN의 키 순서 는 지정되지 않았으며 변경 될 수 있습니다. 매우 간단한 테스트에서는 삽입 순서대로 보이지만 스택과 같이 적절한 부기에서 작성하는 것이 좋습니다. 최신 키만 알고 싶다면 단일 변수 캐시를 사용하십시오.


나는 당신이 이런 식으로 할 수 있다고 생각합니다. 구문이 잘못되었을 수 있습니다. 한동안 C #을 사용하지 않았습니다. 마지막 항목을 얻으려면

Dictionary<string, int>.KeyCollection keys = mydict.keys;
string lastKey = keys.Last();

or use Max instead of Last to get the max value, I dont know which one fits your code better.


One alternative would be a KeyedCollection if the key is embedded in the value.

Just create a basic implementation in a sealed class to use.

So to replace Dictionary<string, int> (which isn't a very good example as there isn't a clear key for a int).

private sealed class IntDictionary : KeyedCollection<string, int>
{
    protected override string GetKeyForItem(int item)
    {
        // The example works better when the value contains the key. It falls down a bit for a dictionary of ints.
        return item.ToString();
    }
}

KeyedCollection<string, int> intCollection = new ClassThatContainsSealedImplementation.IntDictionary();

intCollection.Add(7);

int valueByIndex = intCollection[0];

I agree with the second part of Patrick's answer. Even if in some tests it seems to keep insertion order, the documentation (and normal behavior for dictionaries and hashes) explicitly states the ordering is unspecified.

You're just asking for trouble depending on the ordering of the keys. Add your own bookkeeping (as Patrick said, just a single variable for the last added key) to be sure. Also, don't be tempted by all the methods such as Last and Max on the dictionary as those are probably in relation to the key comparator (I'm not sure about that).


In case you decide to use dangerous code that is subject to breakage, this extension function will fetch a key from a Dictionary<K,V> according to its internal indexing (which for Mono and .NET currently appears to be in the same order as you get by enumerating the Keys property).

It is much preferable to use Linq: dict.Keys.ElementAt(i), but that function will iterate O(N); the following is O(1) but with a reflection performance penalty.

using System;
using System.Collections.Generic;
using System.Reflection;

public static class Extensions
{
    public static TKey KeyByIndex<TKey,TValue>(this Dictionary<TKey, TValue> dict, int idx)
    {
        Type type = typeof(Dictionary<TKey, TValue>);
        FieldInfo info = type.GetField("entries", BindingFlags.NonPublic | BindingFlags.Instance);
        if (info != null)
        {
            // .NET
            Object element = ((Array)info.GetValue(dict)).GetValue(idx);
            return (TKey)element.GetType().GetField("key", BindingFlags.Public | BindingFlags.Instance).GetValue(element);
        }
        // Mono:
        info = type.GetField("keySlots", BindingFlags.NonPublic | BindingFlags.Instance);
        return (TKey)((Array)info.GetValue(dict)).GetValue(idx);
    }
};

The way you worded the question leads me to believe that the int in the Dictionary contains the item's "position" on the Dictionary. Judging from the assertion that the keys aren't stored in the order that they're added, if this is correct, that would mean that keys.Count (or .Count - 1, if you're using zero-based) should still always be the number of the last-entered key?

If that's correct, is there any reason you can't instead use Dictionary<int, string> so that you can use mydict[ mydict.Keys.Count ]?


I don't know if this would work because I'm pretty sure that the keys aren't stored in the order they are added, but you could cast the KeysCollection to a List and then get the last key in the list... but it would be worth having a look.

The only other thing I can think of is to store the keys in a lookup list and add the keys to the list before you add them to the dictionary... it's not pretty tho.


To expand on Daniels post and his comments regarding the key, since the key is embedded within the value anyway, you could resort to using a KeyValuePair<TKey, TValue> as the value. The main reasoning for this is that, in general, the Key isn't necessarily directly derivable from the value.

Then it'd look like this:

public sealed class CustomDictionary<TKey, TValue>
  : KeyedCollection<TKey, KeyValuePair<TKey, TValue>>
{
  protected override TKey GetKeyForItem(KeyValuePair<TKey, TValue> item)
  {
    return item.Key;
  }
}

To use this as in the previous example, you'd do:

CustomDictionary<string, int> custDict = new CustomDictionary<string, int>();

custDict.Add(new KeyValuePair<string, int>("key", 7));

int valueByIndex = custDict[0].Value;
int valueByKey = custDict["key"].Value;
string keyByIndex = custDict[0].Key;

You can also use SortedList and its Generic counterpart. These two classes and in Andrew Peters answer mentioned OrderedDictionary are dictionary classes in which items can be accessed by index (position) as well as by key. How to use these classes you can find: SortedList Class , SortedList Generic Class .


A dictionary may not be very intuitive for using index for reference but, you can have similar operations with an array of KeyValuePair:

ex. KeyValuePair<string, string>[] filters;


Visual Studio's UserVoice gives a link to generic OrderedDictionary implementation by dotmore.

But if you only need to get key/value pairs by index and don't need to get values by keys, you may use one simple trick. Declare some generic class (I called it ListArray) as follows:

class ListArray<T> : List<T[]> { }

You may also declare it with constructors:

class ListArray<T> : List<T[]>
{
    public ListArray() : base() { }
    public ListArray(int capacity) : base(capacity) { }
}

For example, you read some key/value pairs from a file and just want to store them in the order they were read so to get them later by index:

ListArray<string> settingsRead = new ListArray<string>();
using (var sr = new StreamReader(myFile))
{
    string line;
    while ((line = sr.ReadLine()) != null)
    {
        string[] keyValueStrings = line.Split(separator);
        for (int i = 0; i < keyValueStrings.Length; i++)
            keyValueStrings[i] = keyValueStrings[i].Trim();
        settingsRead.Add(keyValueStrings);
    }
}
// Later you get your key/value strings simply by index
string[] myKeyValueStrings = settingsRead[index];

As you may have noticed, you can have not necessarily just pairs of key/value in your ListArray. The item arrays may be of any length, like in jagged array.

참고URL : https://stackoverflow.com/questions/4227/accessing-a-dictionary-keys-key-through-a-numeric-index

반응형