数値インデックスを介した Dictionary.Keys キーへのアクセス
-
08-06-2019 - |
質問
私はを使用しています Dictionary<string, int>
どこ int
キーのカウントです。
ここで、辞書内に最後に挿入されたキーにアクセスする必要がありますが、その名前がわかりません。明らかな試み:
int LastCount = mydict[mydict.keys[mydict.keys.Count]];
機能しません、なぜなら Dictionary.Keys
[]-インデクサは実装されていません。
似たようなクラスはあるのでしょうか?スタックを使用することも考えましたが、それは文字列を格納するだけです。独自の構造体を作成して、 Stack<MyStruct>
, 、しかし、別の代替手段、本質的にキーに []-インデクサーを実装する辞書はあるのだろうか?
他のヒント
使用できます 注文済み辞書.
キーまたはインデックスがアクセスできるキー/バリューペアのコレクションを表します。
辞書はハッシュ テーブルであるため、挿入順序はわかりません。
最後に挿入されたキーを知りたい場合は、辞書を拡張して 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();
または、Last の代わりに Max を使用して最大値を取得します。どちらがコードによく適合するかわかりません。
代替案の 1 つは、 キー付きコレクション キーが値に埋め込まれている場合。
使用するシールされたクラスで基本的な実装を作成するだけです。
それで交換するには 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];
私はパトリックの答えの後半部分に同意します。一部のテストでは挿入順序が維持されているように見えても、ドキュメント (および辞書とハッシュの通常の動作) では、順序が指定されていないと明示的に述べられています。
キーの順序によっては問題が発生するだけです。念のため、独自の簿記を追加してください (Patrick が言ったように、最後に追加したキーに単一の変数を追加するだけです)。また、辞書にある Last や Max などのすべてのメソッドに誘惑されないでください。これらはおそらくキー コンパレータに関連しているためです (それについてはわかりません)。
破損の可能性がある危険なコードを使用することにした場合、この拡張関数はキーを 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);
}
};
あなたの質問の言い方からすると、辞書内の int には辞書上の項目の「位置」が含まれていると思われます。キーが追加された順序で保存されていないというアサーションから判断すると、これが正しければ、keys.Count (ゼロベースを使用している場合は .Count - 1) を引き続き使用する必要があることを意味します。常に最後に入力したキーの番号になりますか?
それが正しい場合、mydict[ mydict.Keys.Count ] を使用できるように、代わりに Dictionary<int, string> を使用できない理由はありますか?
キーが追加された順序で保存されていないことは確かなので、これが機能するかどうかはわかりませんが、KeysCollection を List にキャストして、リストの最後のキーを取得することはできます...しかし、見てみる価値はあるだろう。
他に考えられる唯一のことは、キーをルックアップ リストに保存し、辞書に追加する前にキーをリストに追加することです...それはあまり美しくありません。
キーに関するダニエルの投稿とコメントをさらに詳しく説明するには、いずれにしてもキーは値の中に埋め込まれているため、 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;
SortedList とそれに相当する Generic を使用することもできます。これら 2 つのクラスと Andrew Peters の回答で言及されている OrderedDictionary は、キーだけでなくインデックス (位置) によって項目にアクセスできる辞書クラスです。これらのクラスの使用方法は次のとおりです。 ソートリストクラス , SortedList ジェネリック クラス .
辞書は参照にインデックスを使用するのにあまり直感的ではないかもしれませんが、次の配列を使用して同様の操作を行うことができます。 キー値ペア:
元。KeyValuePair<string, string>[] filters;
Visual Studioの ユーザーの声 へのリンクを与えます 一般的な 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 には必ずしもキーと値のペアだけを含めることはできません。項目配列は、ギザギザ配列のように、任意の長さにすることができます。