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


160

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

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

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

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

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


1
해당 변수를 상자에 넣으면 어떻게됩니까?
Paul Prewett

답변:


222

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

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

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


1
하지 작동하는 것 같다 HashTableElementAt '없이 확장 메서드'ElementAt System.Collections.ICollection '형식의 첫 번째 인수를 받아들이는' '에 대한 정의가 포함되어 있지 않습니다'를 System.Collections.ICollection '볼 수 있습니다
v.oddou

ElementAtOrDefault버전을 사용 하여 예외없는 버전으로 작업 할 수 있습니다 .
Tarık Özgün Güner

22
그런 끔찍한 잘못된 대답이 그처럼 받아 들여지고 찬성하는 것을 보는 것은 무섭습니다. Dictionary<TKey,TValue>문서 에 "키의 순서 Dictionary<TKey, TValue>.KeyCollection가 지정되어 있지 않습니다"라고 나와 있기 때문에 잘못되었습니다 . 순서는 마지막 위치 (에 있는지에 대해 알 수있는 방법이 없다, 정의되어 있지 않다고 mydict.Count -1)
Falanwe

이것은 무서워 ...하지만 주문에 의지 할 수 없다는 내 의혹의 확인을 찾고 있었기 때문에 나에게 도움이되었습니다 ! 감사합니다 @Falanwe
Charlie

3
어떤 경우에는 순서가 관련이 없습니다. 모든 키를 통과했다는 사실입니다.
Royi Mindel

59

OrderedDictionary 를 사용할 수 있습니다 .

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


42
Erhm은 19 번의 공감 후에도 OrderedDictionary가 여전히 색인으로 키를 가져올 수 없다고 언급 한 사람이 없습니까?
Lazlo

1
인덱스가 TKey 여야하는 System.Collections.Generic.SortedDictionary <TKey, TValue> 가 아닌 OrderedDictionary 로 정수 인덱스를 사용하여 값에 액세스 할 수 있습니다. 여기서 인덱스는 TKey 여야합니다.
Maxence

OrderedDictionary 이름은 요소를 추가 된 순서와 동일하게 유지하기 위해이 콜렉션 함수와 관련이 있습니다. 경우에 따라 주문은 정렬과 의미가 같지만이 컬렉션에는 없습니다.
Sharunas Bielskis

18

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

마지막으로 삽입 된 키를 알고 싶다면 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()이를 극복하기 위해 사용할 때 문제가 발생할 수 있으며 순서대로 정렬 된 키 목록을 유지해야합니다.


8

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

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

2
lastKeyInserted를 삽입 된 마지막 값으로 설정하고 있습니다. 마지막으로 삽입 한 키로 설정하거나 변수 및 속성에 대한 더 나은 이름이 필요합니다.
Fantius

6

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

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

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


5

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

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

또는 Last 대신 Max를 사용하여 최대 값을 얻으려면 어느 것이 코드에 더 잘 맞는지 알 수 없습니다.


2
"Last ()"는 확장 메서드이므로 .NET Framework 3.5가 필요하며 .cs 파일 맨 위에 "using System.Linq"를 추가해야합니다.
SuperOli

마지막으로 시도하십시오 (Dist <string, string> 사용시 분명히 :-) KeyValuePair <string, string> last = oAuthPairs.Last (); if (kvp.Key! = last.Key) {_oauth_ParamString = _oauth_ParamString + "&"; }
Tim Windsor

4

Patrick의 답변의 두 번째 부분에 동의합니다. 일부 테스트에서 삽입 순서를 유지하는 것처럼 보이지만 설명서 (및 사전 및 해시의 일반적인 동작)에 명시 적으로 순서가 지정되지 않았습니다.

당신은 키의 순서에 따라 문제를 요구하고 있습니다. Patrick이 말했듯이 마지막 부기 키에 대한 단일 변수 인 자신의 부기를 추가하십시오. 또한 사전의 Last 및 Max와 같은 모든 방법으로 키 비교기와 관련이 있기 때문에 유혹을받지 마십시오 (확실하지 않습니다).


4

손상 될 위험이있는 코드를 사용하기로 결정한 경우이 확장 함수는 Dictionary<K,V>내부 인덱싱에 따라 키를 가져옵니다 (Mono 및 .NET의 경우 현재 Keys속성 을 열거 한 순서와 동일한 순서로 나타남) ).

Linq :를 사용하는 것이 훨씬 바람직 dict.Keys.ElementAt(i)하지만이 함수는 O (N)을 반복합니다. 다음은 O (1)이지만 반사 성능 패널티가 있습니다.

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

흠, 답변을 개선하기 위해 편집하면 공감대를 얻었습니다. 코드가 (분명히) 끔찍하다는 것을 명확하게 밝히지 않았습니까?
Glenn Slayden

4

키가 값에 포함 된 경우 KeyedCollection이 대안이 될 수 있습니다 .

사용할 봉인 클래스에서 기본 구현을 작성하십시오.

따라서 바꾸려면 Dictionary<string, int>(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];

키에 대한 귀하의 의견에 대해서는 이것에 대한 후속 답변을 참조하십시오.
takrl

3

당신이 그 질문에 대해 말한 방식은 사전의 int에 사전에 항목의 "위치"가 포함되어 있다고 믿게합니다. 키가 추가 된 순서대로 저장되지 않았다는 주장에서 판단 할 때 이것이 정확하다면 키를 의미합니다. 항상 마지막으로 입력 한 키의 번호입니까?

맞다면 mydict [mydict.Keys.Count]를 사용할 수 있도록 Dictionary <int, string>을 대신 사용할 수없는 이유가 있습니까?


2

키가 추가 된 순서대로 저장되지 않았기 때문에 이것이 작동하는지 모르겠지만 KeysCollection을 List로 캐스팅 한 다음 목록의 마지막 키를 가져올 수 있습니다 ... 그러나 살펴볼 가치가 있습니다.

내가 생각할 수있는 유일한 다른 것은 키를 조회 목록에 저장하고 사전에 키를 추가하기 전에 키를 목록에 추가하는 것입니다.


@Juan : KeyCollection에는 .Last () 메소드가 없습니다
lomaxx

코드를 테스트하지는 않았지만이 방법은 [MSDN] [1] 다른 버전의 프레임 워크에 문서화되어 있습니까? [1] : msdn.microsoft.com/en-us/library/bb908406.aspx
Juan

2 년 늦었지만 누군가 도움이 될 수 있습니다 ... 아래 Juan의 게시물에 대한 내 답변을 참조하십시오. Last ()는 확장 메소드입니다.
SuperOli

2

키가 값에 포함되어 있기 때문에 Daniels 게시물과 키에 대한 그의 의견을 확장하기 위해 어쨌든 키를 값으로 사용할 수 KeyValuePair<TKey, TValue>있습니다. 이것에 대한 주된 이유는 일반적으로 키가 반드시 값에서 직접 파생 될 필요는 없다는 것입니다.

그러면 다음과 같이 보일 것입니다 :

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

이전 예제에서와 같이 이것을 사용하려면 다음을 수행하십시오.

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;

2

사전은 참조 용 색인을 사용하는 데있어 직관적이지 않을 수 있지만 KeyValuePair 배열을 사용하여 유사한 조작을 수행 할 수 있습니다 .

전의. KeyValuePair<string, string>[] filters;



1

Visual Studio의 UserVoice 는 dotmore의 일반적인 OrderedDictionary 구현 에 대한 링크를 제공합니다 .

그러나 인덱스별로 키 / 값 쌍만 가져오고 키로 값을 가져 오지 않아도되는 경우 간단한 트릭을 사용할 수 있습니다. 다음과 같이 일부 일반 클래스 (ListArray라고 함)를 선언하십시오.

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

생성자로 선언 할 수도 있습니다.

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

예를 들어, 파일에서 일부 키 / 값 쌍을 읽은 다음 나중에 색인으로 가져 오기 위해 읽은 순서대로 저장하려고합니다.

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

알다시피, ListArray에 반드시 키 / 값 쌍만있는 것은 아닙니다. 항목 배열은 들쭉날쭉 한 배열과 같이 임의의 길이 일 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.