CSharp:Dictionary
From Aino Wiki
Ottimi esempi: Dot Net perls
Contents
Es. 1
Supponendo di creare una cache di documenti HTML in memoria il cui valore è oggetto del HTML Agility Pack:
using System.Collections.Generic; ... private static Dictionary<string, HtmlAgilityPack.HtmlDocument> DictEmailTemplate = new Dictionary<string, HtmlAgilityPack.HtmlDocument>(); ... HtmlAgilityPack.HtmlDocument docHTML = new HtmlAgilityPack.HtmlDocument(); // Verifica se il Template è stato caricato prima if (!DictEmailTemplate.ContainsKey(fullPathFileNameTemplate)) { // Caricamenteo File HtmlAgilityPack.HtmlDocument docHTMLNew = new HtmlAgilityPack.HtmlDocument(); docHTMLNew.Load(fullPathFileNameTemplate); DictEmailTemplate.Add(fullPathFileNameTemplate, docHTMLNew); } else { docHTML = DictEmailTemplate[fullPathFileNameTemplate]; }
Ciclo sulle coppie
Brutalmente copiato da DotNet perl, in attesa di mia sistemazine.
using System; using System.Collections.Generic; class Program { static void Main() { // Example Dictionary again Dictionary<string, int> d = new Dictionary<string, int>() { {"cat", 2}, {"dog", 1}, {"llama", 0}, {"iguana", -1} }; // Loop over pairs with foreach foreach (KeyValuePair<string, int> pair in d) { Console.WriteLine("{0}, {1}", pair.Key, pair.Value); } // Use var keyword to enumerate dictionary foreach (var pair in d) { Console.WriteLine("{0}, {1}", pair.Key, pair.Value); } } }
Output
cat, 2 dog, 1 llama, 0 iguana, -1 cat, 2 dog, 1 llama, 0 iguana, -1
Rimozione
Rimozione per valore
Metodo con scansione lineare:foreach (KeyValuePair<string, UA_AISInfo> ai in m_DctUA_AIS) { if (ai.Value.NodeDisplayName == id) { m_DctUA_AIS.Remove(ai.Key); break; } }
private static void RemoveByValue<TKey,TValue>(Dictionary<TKey, TValue> dictionary, TValue someValue){
List<TKey> itemsToRemove = new List<TKey>(); foreach (var pair in dictionary) { if (pair.Value.Equals(someValue)) itemsToRemove.Add(pair.Key); }
foreach (TKey item in itemsToRemove) { dictionary.Remove(item); }}
Dictionary<int, string> dictionary = new Dictionary<int, string>(); dictionary.Add(1, "foo"); dictionary.Add(2, "foo"); dictionary.Add(3, "bar"); string someValue = "foo"; RemoveByValue(dictionary, someValue);
Es.1
Stesso risultato col metodo diretto (non funziona):var item = m_DctUA_AIS.First(x => x.Value.NodeDisplayName == id); m_DctUA_AIS.Remove(item.Key);
Rimozione multipla
Esempio preso da StackOverflow:foreach ( var s in MyCollection.Where(kv => kv.Value.Member == foo).ToList() ) { MyCollection.Remove(s.Key); }
Pare elimini il problema della eliminazione durante l'enumerazione della lista stessa. Il ToList()
alla fine FORZA l'enumerazione prima che il ForEach
inizi le azioni previste.
Ordinamento
Da dotNetPerls, usando LinQ:using System; using System.Collections.Generic; using System.Linq; class Program { static void Main() { var items = new Dictionary<int, int>(); items.Add(-1, 0); items.Add(0, 1); items.Add(-2, 0); items.Add(3, 1); // Use OrderBy method. foreach (var item in items.OrderBy(i => i.Key)) { Console.WriteLine(item); } } }
Posizionamento
Accedere ad un elemento
Per avere chiave e valore di un elemento mediante indice numerico, usando Linq:
Dictionary<string,string> dizionario = new Dictionary<string,string>(); //.. riempio il dizionario var primo = dizionario.First(); // coppia ordinata del dizionario in posizione uno string key = primo.Key; Dictionary<string,string> val = first.Value;
Accedere all'elemento in posizione n
La premessa è che la richiesta è mal posta in quanto un dizionario non implementa l'accesso posizionale e sarebbe stato meglio usare una lista. Tuttavia riporto esempio da stackoverflow.
cipher = new Dictionary<char,int>; cipher.Add( 'a', 324 ); cipher.Add( 'b', 553 ); cipher.Add( 'c', 915 ); // .... int n = 0; // Elemento desiderato int nthValue = cipher[cipher.Keys.ToList()[n]];
Soluzioni varie
Aggiunta o aggiornamento
SOLUZIONE FONDAMENTALE!
dato un dizionario esiste un modo COMPATTO per aggiungere un elemento se non esiste e per aggiornarlo nel caso esista già. Questo eviterà errori di aggiunta per elementi che si da per scontato che non esistano. E' un metodo potente ma altrettanto pericoloso quindi va usato con consapevolezza.
foreach (KeyValuePair<string, NodeSubscribedInfo> dctItmNS in dctNodeSubscrInfo) { // modo compatto per aggiungere un elemento se non esiste altrimenti lo si aggiunge m_DctMainNodeSubscribed[dctItmNS.Key] = dctItmNS.Value; // <----------- !!!!!!!!!!!! }
Aggiunta di un dizionario ad un altro
Da stackoverflow:foreach(var newAnimal in NewAnimals) Animals.Add(newAnimal.Key,newAnimal.Value);
public static void AddRange<T>(this ICollection<T> target, IEnumerable<T> source){
if(target==null) throw new ArgumentNullException("target"); if(source==null) throw new ArgumentNullException("source"); foreach(var element in source) target.Add(element);}
Selezioni
Selezionare un sottoDizionario da uno di partenza
private Dictionary<string, ItemDictionaryInfo> m_ItemsDictionarySubscription = new Dictionary<string, ItemDictionaryInfo>(); //... foreach (KeyValuePair<string, ItemDictionaryInfo> pItemSubscription in m_ItemsDictionarySubscription .Where(x => !string.IsNullOrEmpty(x.Value.SubscriptionItemsNamePattern))) { // qualcosa da fare... }
Selezionare un sottoinsieme di coppie del dizionario
Da stackoverflow, usandoToDictionary()
:Dictionary<string, Object> Data; Dictionary<string, int> IntData = Data.Where(k => k.Value is int) .ToDictionary(kv => kv.Key, kv => (int) kv.Value);
Caricamento Dizionario con un XML
Il metodo è fatto per caricare due tipi di XML diversi, ovvero con diverso numero di attributi per elemento:Primo XML:<?xml version="1.0" encoding="utf-8" ?> <OpcServerConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <key key="#Wind" subPath="Wind,Meteorology/Wind" attributes="Speed,Angle" /> <key key="#AirTemperature" subPath="AirTemperature,Meteorology/Temperature/Air" attributes="Temperature" /> <key key="#AirPressure" subPath="AirPressure,Meteorology/Barometer" attributes="Pressure" /> <key key="#AirHumidity" subPath="AirHumidity,Meteorology/Humidity" attributes="Relative" /> <key key="#AIS" subPath="AIVDM,NavigationalData/AIS/Class_A|AIVDO,NavigationalData/AIS/Own_Ship" attributes="AttitudeData.Bearing,AttitudeData.Course,AttitudeData.Distance,AttitudeData.Heading,AttitudeData.Speed,Callsign,Destination,Draught,ETA,TypeOfShip,ShipsDimension.rpoint_a,ShipsDimension.rpoint_b,ShipsDimension.rpoint_c,ShipsDimension.rpoint_d,IMONR,MMSI,Position.Latitude,Position.Longitude,Position_Accuracy,ROT_Valid,RateOfTurn,ShipsName,Status,TypeOfShip"/> </OpcServerConfig>
<?xml version="1.0" encoding="utf-8" ?><OpcServerConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<key key="#Wind"
type="FixedItems" attributes="Speed,Angle" />
<key key="#Waypoints"
type="Event" itemsNamePattern="D" attributes="Identifier,Position.Latitude,Position.Longitude" />
</OpcServerConfig>
public static Dictionary<string, ItemDictionaryInfo> LoadItemsDictionary(string path){ Dictionary<string, ItemDictionaryInfo> itemsDictionary = new Dictionary<string, ItemDictionaryInfo>();
XDocument xmlItemMapping = XDocument.Load(path);
string key = string.Empty; string subPath = string.Empty; string attributes = string.Empty; ItemDictionaryInfo itemMapping = new ItemDictionaryInfo();
foreach (XElement element in xmlItemMapping.Descendants("key")) { key = element.Attribute("key").Value; itemMapping = new ItemDictionaryInfo();
#region Acquisizione VALORI del dizionario #region Info esclusive DIZIONARIO PRINCIPALE if (element.Attribute("subPath") != null) { subPath = element.Attribute("subPath").Value; } else { subPath = string.Empty; } if (!string.IsNullOrEmpty(subPath)) { string[] arrSubPath = subPath.Split('|'); itemMapping.SubPaths = new Dictionary<string, string>(); foreach (string s in arrSubPath) { string[] arrItemsSubPath = s.Split(','); itemMapping.SubPaths.Add(arrItemsSubPath[0], arrItemsSubPath[1]); } } if (element.Attribute("itemsNamePattern") != null) { itemMapping.SubscriptionItemsNamePattern = element.Attribute("itemsNamePattern").Value; } #endregion
#region Info esclusive SUBSCRIPTION if (element.Attribute("type") != null) { itemMapping.SubscriptionType = EnumsAcquisitor.ParseEnum<EnumsAcquisitor.EnumSubscriptionType>(element.Attribute("type").Value); } else { itemMapping.SubscriptionType = EnumsAcquisitor.EnumSubscriptionType.FixedItems; } #endregion
// Attributi COMUNI attributes = element.Attribute("attributes").Value; itemMapping.AttributesCSV = attributes; #endregion
itemsDictionary.Add(key, itemMapping); } return itemsDictionary;}
public class ItemDictionaryInfo { #region Properties /// <summary> /// In riferimento all'esempio di sopra, potrà contenere le info associate alla chiave: /// #AIS /// </summary> public Dictionary<string, string> SubPaths { get; set; } /// <summary> /// Indica se la sottoscrizione è di tipo "FixedItems" o "Event". La prima è quella più /// semplice costituita da un elenco di nodi con relativi attributi da acquisire con subscription /// mentre la seconda riguarda informazioni che pur essendo sotto un percorso fisso posso variare /// cioè essere aggiunti o sottratti ciascun elemento però ha un insieme fisso di attributi da acquisire. /// </summary> public EnumsAcquisitor.EnumSubscriptionType SubscriptionType { get; set; } /// <summary> /// Per identificare l'appartenenza di un elemento ad una entità o meno lo si fa (cosa introdotta DOPO) /// in base alla radice del nome dell'item sottoscritto e che ha subito un cambio stato (nuove info). /// Es. è un AIS se Inizia con 'M', è un ARPA (target) se inizia per 'Target', è un Waypoint se inizia per 'D', etc /// </summary> public string SubscriptionItemsNamePattern { get; set; } /// <summary> /// In riferimento all'esempio di sopra, potrà contenere: /// Destination,Draught,IMONR,MMSI,Position.Latitude,Position.Longitude,RateOfTurn,ShipsName,Status /// </summary> public string AttributesCSV { get; set; } #endregion }
Conversione da Dictionary a List
Per convertire dun dizionario in una lista delle sue chiavi
listNumber = dicNumber.Select(kvp => kvp.Key).ToList();
oppure
listNumber = dicNumber.Keys.ToList();
Ricerca
Sostanzialmente si usa il metodo ContainsKey
, poi per avere la posizione assoluta si trasforma il dizionario in LISTA...
(probabilmente per queste operazioni sarebbe stato meglio altra struttura rispetto al dizionario ma per picoli insiemi di dat magari è comodo).
public static void Main() { string fileName = string.Empty; string fileNameFullPath = string.Empty; m_DictCurrItemDocFileLst.Add("fileName1", "path\\fileName1"); m_DictCurrItemDocFileLst.Add("fileName2", "path\\fileName2"); m_DictCurrItemDocFileLst.Add("fileName3", "path\\fileName3"); m_DictCurrItemDocFileLst.Add("fileName4", "path\\fileName4"); m_DictCurrItemDocFileLst.Add("fileName5", "path\\fileName5"); fileName = "fileName5"; if (m_DictCurrItemDocFileLst.ContainsKey(fileName)) { m_IndxCurrItemDocFileLst = m_DictCurrItemDocFileLst.Keys.ToList().IndexOf(fileName); } else { m_IndxCurrItemDocFileLst+= -1; } Console.WriteLine("File '{0}', in posizione '{1}'!", fileName, m_IndxCurrItemDocFileLst); }
Mappa e Link
Hashtable Parole chiave:ToDictionary , LinQ