数値インデックスを介してDictionary.Keysキーにアクセスする


160

私は、キーの数であるDictionary<string, int>where を使用してintいます。

ここで、最後に挿入した辞書内のキーにアクセスする必要がありますが、その名前がわかりません。明らかな試み:

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

Dictionary.Keys[]インデクサーを実装していないため、機能しません。

似たようなクラスはないかしら?スタックの使用を考えましたが、それは文字列のみを格納します。独自の構造体を作成してからを使用することもできますが、Stack<MyStruct>別の代替手段、本質的にはキーに[]インデクサーを実装するディクショナリがあるのだろうか。


1
その変数をボックス化するとどうなりますか?
Paul Prewett、2014

回答:


222

@Falanweがコメントで指摘しているように、次のようなことは正しくありません

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

辞書内のキーの順序に依存するべきではありません。注文が必要な場合は、この回答で提案されているように、OrderedDictionaryを使用する必要があります。このページの他の回答も興味深いです。


1
HashTableSystem.Collections.ICollectionで動作しないようです 'ElementAt'の定義が含まれておらず、タイプ 'System.Collections.ICollection'の最初の引数を受け入れる拡張メソッド 'ElementAt'が見つかりません
v.oddou

ElementAtOrDefaultversionを使用して、例外のないバージョンで作業できます。
–TarıkÖzgünGüner2015

22
そのような露骨に間違った答えが受け入れられ、そのように賛成されたことを見るのは恐ろしいことです。Dictionary<TKey,TValue>ドキュメントには「のキーの順序Dictionary<TKey, TValue>.KeyCollectionは指定されていません」と記載されているため、これは誤りです。順序が定義されていないため、最後の位置にあるかどうかを確認する方法はありません(mydict.Count -1
Falanwe

これは怖いですが、注文に数えることができないという私の疑いの確認を探していたので、私にとっては役に立ちます!!! ありがとう@Falanwe
チャーリー

3
順序によっては関係ない場合もあります-すべてのキーを確認したという事実だけです。
Royi Mindel

59

OrderedDictionaryを使用できます。

キーまたはインデックスによってアクセス可能なキー/値ペアのコレクションを表します。


42
Erhm、19回の賛成投票の後、まだOrderedDictionaryがインデックスでキーを取得できないことについて誰も言及していませんか?
Lazlo

1
OrderedDictionaryを使用して整数インデックスで値にアクセスできますが、インデックスがTKeyである必要があるSystem.Collections.Generic.SortedDictionary <TKey、TValue>ではアクセスできません
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 2010年

最後にこれを試してください(Dist <string、string>を使用している場合は明らかに:-) KeyValuePair <string、string> last = oAuthPairs.Last(); if(kvp.Key!= last.Key){_oauth_ParamString = _oauth_ParamString + "&"; }
Tim Windsor

4

私はパトリックの答えの後半に同意します。一部のテストでは挿入順序が維持されているように見えても、ドキュメント(およびディクショナリとハッシュの通常の動作)では、順序は明示されていません。

キーの順序によっては、問題を求めているだけです。確認のために、独自の簿記を追加します(パトリックが言ったように、最後に追加されたキーの変数は1つだけです)。また、ディクショナリのLastやMaxなどのすべてのメソッドに誘惑されないでください。これらはおそらくキーコンパレーターに関連しているためです(それについてはわかりません)。


4

破損しやすい危険なコードを使用する場合、この拡張関数はDictionary<K,V>内部のインデックスに従ってMono からキーをフェッチします(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

キーが値に埋め込まれている場合、1つの代替案は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 '20

3

質問の言い方から、辞書のintには辞書のアイテムの「位置」が含まれていると思います。キーが追加された順序で格納されていないという主張から判断して、これが正しい場合、keys.Count(またはゼロベースを使用している場合は.Count-1)がまだ必要であることを意味します常に最後に入力したキーの番号ですか?

それが正しい場合、代わりにDictionary <int、string>を使用してmydict [mydict.Keys.Count]を使用できない理由はありますか?


2

キーが追加された順序で格納されていないことは確かなので、これが機能するかどうかはわかりませんが、KeysCollectionをリストにキャストして、リストの最後のキーを取得できます...しかし、それは一見の価値があります。

他に考えられる唯一のことは、キーをルックアップリストに格納し、辞書に追加する前にリストにキーを追加することです。それはかなり簡単ではありません。


@Juan:KeyCollectionに.Last()メソッドがない
lomaxx

コードはテストしませんでしたが、メソッドは[MSDN] [1]に文書化されています。フレームワークの別のバージョンでしょうか?[1]:msdn.microsoft.com/en-us/library/bb908406.aspx
フアン

2年遅れますが、誰かを助けるかもしれません...以下のフアンの投稿への私の返信を参照してください。Last()は拡張メソッドです。
SuperOli 2010年

2

ダニエルズの投稿とキーに関する彼のコメントを拡張すると、とにかくキーは値の中に埋め込まれているKeyValuePair<TKey, TValue>ので、値としてaを使用することに頼ることができます。これの主な理由は、一般に、キーは必ずしも値から直接導出できるとは限らないことです。

その後、次のようになります。

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;


2

また、SortedListとその一般的な対応物を使用することもできます。これらの2つのクラスと、Andrew Petersの回答で述べたOrderedDictionaryは、インデックスクラス(位置)とキーによって項目にアクセスできる辞書クラスです。これらのクラスの使用方法を見つけることができます:SortedList ClassSortedList Generic Class


1

Visual StudioのUserVoiceは、dotmoreによる一般的なOrderedDictionary実装へのリンクを提供します。

ただし、インデックスでキーと値のペアを取得するだけで、キーで値を取得する必要がない場合は、1つの簡単な方法を使用できます。次のようにいくつかのジェネリッククラス(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.