I have a piece of code that represent Dictionary and search key array.
Dictionary<string, string> items = new Dictionary<string, string>()
{
{"1","Blue"},
{"2","Green"},
{"3","White"}
};
string[] keys = new[] { "1", "2", "3", "4" };
How to safely avoid the run time error when i pass a key that is not present in dictionary?
How to safely avoid the run time error when i pass a key that is not present in dictionary?
You haven't shown how you're currently trying to do it, but you can use Dictionary<,>.TryGetValue:
foreach (string candidate in keys)
{
string value;
if (items.TryGetValue(candidate, out value))
{
Console.WriteLine("Key {0} had value {1}", candidate, value);
}
else
{
Console.WriteLine("No value for key {0}", candidate);
}
}
Use either ContainsKey or TryGetValue to check the existence of a key.
string val = string.Empty;
foreach (var ky in keys)
{
if (items.TryGetValue(ky, out val))
{
Console.WriteLine(val);
}
}
or
foreach (var ky in keys)
{
if (items.ContainsKey(ky))
{
Console.WriteLine(items[ky]);
}
}
Though TryGetValue is faster than ContainsKey use it when you want to pull the value from dictionary.if you want to check the existence of key use ContainsKey.
Related
I have a Dictionary<string, string> object where values are stored that look like this:
examplePlanet : defaultText0
examplePlanet* : defaultText1
examplePlanet** : defaultText2
examplePlanetSpecificlocationA : specificAText0
examplePlanetSpecificlocationA* : specificAText1
examplePlanetSpecificlocationB : specificBText
And I have a string filter that matches one of these keys or is a subset of a key.
This filter has the form planetLocation, which can be split into planet and location.
My goal is to create a list of values where the filter is matched in this way: if planetLocation exists in the dictionary, add its value, and all values where the key matches but has extra *'s, to a list.
If planetLocation does not exist, only add values where the key matches the planet part of the filter (with possible extra *'s).
Basically, I want all values where the filter matches the key as much as possible.
Examples:
examplePlanetSpecificlocationA gives [specificAText0, specificAText1]
examplePlanetSpecificlocationB gives [specificBText]
examplePlanetSpecificlocationC gives [defaultText0, defaultText1, defaultText2]
I have already tried (among other things that didn't work) this:
private List<string> filteredResults;
///<summary>Filters dictionaries and returns a list of values</summary>
private List<string> GetFilteredResults(Dictionary<string, string> inputdictionary, string filter)
{
List<string> _filteredResults = new List<string>();
foreach (KeyValuePair<string, string> entry in inputdictionary)
{
if (entry.Key.Contains(filter))
{
_filteredResults.Add(entry.Value);
}
}
return _filteredResults;
}
public void main()
{
//stuff happens here that assigns a value to filterPlanet and filterLocation
filteredResults = new List<string>();
filteredResults = GetFilteredResults(exampledictionary, filterPlanet + filterLocation);
if (filteredResults.Count == 0)
{
filteredResults = GetFilteredResults(exampledictionary, filterPlanet);
}
//do stuff with the filtered results
}
This almost worked, but returns all values where the key contains filterPlanet and not just filterPlanet itself plus possible *'s. I'm not sure how to make this function do what I want, and even if it somehow works I'm sure there is a more efficient way of filtering than this. Could you please help me here?
I wouldn't use the *'s as a way of differentiating multiple values for the same key, and I'd keep filterPlanet and filterLocation separate. That way you can use a simple O(1) dictionary lookup, rather than iterating across all keys, doing substring searching, etc.
public class PlanetFilterer
{
private readonly Dictionary<string, List<string>> lookup = new Dictionary<string, List<string>>();
public PlanetFilterer(IEnumerable<(string filter, string value)> filters)
{
foreach (var (filter, value) in filters)
{
var filterWithoutStars = filter.TrimEnd('*');
if (!lookup.TryGetValue(filterWithoutStars, out var values))
{
values = new List<string>();
lookup[filterWithoutStars] = values;
}
values.Add(value);
}
}
public IReadOnlyList<string> Lookup(string planet, string location)
{
List<string> results;
if (lookup.TryGetValue(planet + location, out results))
{
return results;
}
if (lookup.TryGetValue(planet, out results))
{
return results;
}
return Array.Empty<string>();
}
}
Usage:
var filters = new[]
{
("examplePlanet", "defaultText0"),
("examplePlanet*", "defaultText1"),
("examplePlanet**", "defaultText2"),
("examplePlanetSpecificlocationA", "specificAText0"),
("examplePlanetSpecificlocationA*", "specificAText1"),
("examplePlanetSpecificlocationB", "specificBText"),
};
var filterer = new PlanetFilterer(filters);
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationA")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationB")));
Console.WriteLine(string.Join(", ", filterer.Lookup("examplePlanet", "SpecificlocationC")));
Try it online
I am trying to add values to a dictionary as they get determined based on certain conditions. The app has to loop through each line and once a certain condition has been met then a value must be added to the dictionary. Here is the code that is task with looping through the lines and determine values to be added.
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
//AddErrorFinesToFile(line, fineNumber);
AddFinesToDictonary(fineNumber, line);
continue;
}
}
Then below is the actual method signature and its code, in this method I am simply trying to add values to the dictionary as they come.
public Dictionary<string, string> AddFinesToDictonary(string fineNumber, string errorLine)
{
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
erroredLines.Add(fineNumber, errorLine);
return erroredLines;
}
The only problem that seems to arise here is, only the latest value gets added to the dictionary, meaning the previous added value gets overwritten.
Make erroredLines as global scope.
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
//AddErrorFinesToFile(line, fineNumber);
AddFinesToDictonary(fineNumber, line);
continue;
}
}
public void AddFinesToDictonary(string fineNumber, string errorLine)
{
erroredLines.Add(fineNumber, errorLine);
// return erroredLines;
}
And also no need to return erroredLines dictionary.
What about this;
Dictionary<string, string> erroredLines = new Dictionary<string, string>();
foreach (var line in this.FileLines)
{
count++;
string[] bits = line.Split(',');
fineNumber = bits[0].Trim();
int length = bits.Length;
if (length == 9)
{
//other processing gets done here, code not included as its of no interest for this question
}
else
{
erroredLines.Add(fineNumber, line);
continue;
}
}
after foreach you can use erroredLines dictionary.
The reason is every time you add data to the Directory you create a new one instead of adding data to the exists one
There are two choices you could make:
make a Dictionary out of the function
pass the Dictionary as an out ref to the function
#Hameed Syed 's answer has already given the first one (option 1).
Here is how you could pass the Dictionary as a ref parameter (out) to the function (option 2):
public void AddFinesToDictonary(out Dictionary<string,string>dict, string fineNumber, string errorLine)
{
dict.Add(fineNumber, errorLine);
}
Struggling with this code, very new to c# and I've tried multiple answers on stackoverflow before writing this question so please hear me out...
var onlinePlayers = new Dictionary<int, Dictionary<string,string>>();
var count = 0;
foreach (BasePlayer player in BasePlayer.activePlayerList)
{
onlinePlayers.Add(
count,
new Dictionary<string, string> {
{ "SteamID", player.userID.ToString() },
{ "PlayerName", player.displayName.ToString() },
{ "IPAddress", Regex.Replace(player.net.connection.ipaddress, #":{1}[0-9]{1}\d*", "").ToString }
}
);
count += 1;
}
foreach (var k in onlinePlayers.Keys)
{
foreach (KeyValuePair<string, string> pair in onlinePlayers[k])
{
Console.WriteLine("{0}: {1}", pair.Key, pair.Value);
}
}
I'm expecting to see
SteamID: 1231231221321
PlayerName: HelloWorld
IPAddress: 127.0.0.1
for each item in the collection.
Compiler error:
Error while compiling StatLogger.cs(38,111): error CS1503: Argument #2 cannot
convert method group expression to type string
You're missing the parentheses () at the end of your .ToString().
{ "IPAddress", Regex.Replace(player.net.connection.ipaddress,
#":{1}[0-9]{1}\d*", "").ToString }
Update :
Also player.net.connection.ipaddress.ToString()
The above will turn the IP into a string and that should prevent the error.
You're trying to enumerate the Dictionary's key, rather than the Dictionary itself.
Try this:
foreach (player in onlinePlayers) {
foreach (playerInfo in player.Value) {
Console.WriteLine("{0}: {1}", playerInfo.Key, playerInfo.Value);
}
}
See if that helps.
I keep getting an error with the following code:
Dictionary<string, string> rct3Features = new Dictionary<string, string>();
Dictionary<string, string> rct4Features = new Dictionary<string, string>();
foreach (string line in rct3Lines)
{
string[] items = line.Split(new String[] { " " }, 2, StringSplitOptions.None);
rct3Features.Add(items[0], items[1]);
////To print out the dictionary (to see if it works)
//foreach (KeyValuePair<string, string> item in rct3Features)
//{
// Console.WriteLine(item.Key + " " + item.Value);
//}
}
The error throws an ArgumentException saying,
"An item with the same key has already been added."
I am unsure after several Google searches how to fix this.
Later in the code I need to access the dictionary for a compare function:
Compare4To3(rct4Features, rct3Features);
public static void Compare4To3(Dictionary<string, string> dictionaryOne, Dictionary<string, string> dictionaryTwo)
{
//foreach (string item in dictionaryOne)
//{
//To print out the dictionary (to see if it works)
foreach (KeyValuePair<string, string> item in dictionaryOne)
{
Console.WriteLine(item.Key + " " + item.Value);
}
//if (dictionaryTwo.ContainsKey(dictionaryOne.Keys)
//{
// Console.Write("True");
//}
//else
//{
// Console.Write("False");
//}
//}
}
This function isn't completed, but I am trying to resolve this exception. What are the ways I can fix this exception error, and keep access to the dictionary for use with this function? Thank you
This error is fairly self-explanatory. Dictionary keys are unique and you cannot have more than one of the same key. To fix this, you should modify your code like so:
Dictionary<string, string> rct3Features = new Dictionary<string, string>();
Dictionary<string, string> rct4Features = new Dictionary<string, string>();
foreach (string line in rct3Lines)
{
string[] items = line.Split(new String[] { " " }, 2, StringSplitOptions.None);
if (!rct3Features.ContainsKey(items[0]))
{
rct3Features.Add(items[0], items[1]);
}
////To print out the dictionary (to see if it works)
//foreach (KeyValuePair<string, string> item in rct3Features)
//{
// Console.WriteLine(item.Key + " " + item.Value);
//}
}
This simple if statement ensures that you are only attempting to add a new entry to the Dictionary when the Key (items[0]) is not already present.
If you want "insert or replace" semantics, use this syntax:
A[key] = value; // <-- insert or replace semantics
It's more efficient and readable than calls involving "ContainsKey()" or "Remove()" prior to "Add()".
So in your case:
rct3Features[items[0]] = items[1];
As others have said, you are adding the same key more than once. If this is a NOT a valid scenario, then check Jdinklage Morgoone's answer (which only saves the first value found for a key), or, consider this workaround (which only saves the last value found for a key):
// This will always overwrite the existing value if one is already stored for this key
rct3Features[items[0]] = items[1];
Otherwise, if it is valid to have multiple values for a single key, then you should consider storing your values in a List<string> for each string key.
For example:
var rct3Features = new Dictionary<string, List<string>>();
var rct4Features = new Dictionary<string, List<string>>();
foreach (string line in rct3Lines)
{
string[] items = line.Split(new String[] { " " }, 2, StringSplitOptions.None);
if (!rct3Features.ContainsKey(items[0]))
{
// No items for this key have been added, so create a new list
// for the value with item[1] as the only item in the list
rct3Features.Add(items[0], new List<string> { items[1] });
}
else
{
// This key already exists, so add item[1] to the existing list value
rct3Features[items[0]].Add(items[1]);
}
}
// To display your keys and values (testing)
foreach (KeyValuePair<string, List<string>> item in rct3Features)
{
Console.WriteLine("The Key: {0} has values:", item.Key);
foreach (string value in item.Value)
{
Console.WriteLine(" - {0}", value);
}
}
To illustrate the problem you are having, let's look at some code...
Dictionary<string, string> test = new Dictionary<string, string>();
test.Add("Key1", "Value1"); // Works fine
test.Add("Key2", "Value2"); // Works fine
test.Add("Key1", "Value3"); // Fails because of duplicate key
The reason that a dictionary has a key/value pair is a feature so you can do this...
var myString = test["Key2"]; // myString is now Value2.
If Dictionary had 2 Key2's, it wouldn't know which one to return, so it limits you to a unique key.
That Exception is thrown if there is already a key in the dictionary when you try to add the new one.
There must be more than one line in rct3Lines with the same first word. You can't have 2 entries in the same dictionary with the same key.
You need to decide what you want to happen if the key already exists - if you want to just update the value where the key exists you can simply
rct3Features[items[0]]=items[1]
but, if not you may want to test if the key already exists with:
if(rect3Features.ContainsKey(items[0]))
{
//Do something
}
else
{
//Do something else
}
I suggest .NET's TryAdd:
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.dictionary-2.tryadd?view=net-7.0
I suggest a extension method for environments where .NET's TryAdd is not available:
public static class DictionaryUtils
{
/// <summary>
/// Prevents exception "Item with Same Key has already been added".
/// </summary>
public static void TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
if (!dictionary.ContainsKey(key))
{
dictionary.Add(key, value);
}
}
}
Clear the dictionary before adding any items to it. I don't know how a dictionary of one object affects another's during assignment but I got the error after creating another object with the same key,value pairs.
NB:
If you are going to add items in a loop just make sure you clear the dictionary before entering the loop.
This question already has answers here:
Remove Item in Dictionary based on Value
(6 answers)
Closed 9 years ago.
The question should be clear from the title itself. I need to check if an item exist in the dictionary and remove it from the dictionary in C#. The only catch is that i have to do this using only the value item and not the key.
The declaration is as below:
IDictionary<string, myCustomClassObject> clients = new IDictionary<string, myCustomClassObject>();
Now i fill in the dictionary by:
clients["key"] = myCustomClassObject1;
Now how can i find and remove this item myCustomClassObject1 from my Dictionary. I only want to use the value item and not the key
Is this doabale...if so please guide...
regards
Edit: Thank you all....got valuable comments...probably have some thinking to do ...thanks
It depends on how you need it to perform. If you can accept O(N) performance, you could just do something like:
foreach(var pair in clients) {
if(pair.Value == expected) {
clients.Remove(pair.Key);
break;
}
}
However, if you need faster you would need two dictionaries - one the reverse of the other (i.e. keyed by the instances). So when adding, you would do:
clientsByKey.Add(key, value);
clientsByValue.Add(value, key);
so you can do (to remove-by-value):
string key;
if(clientsByValue.TryGetValue(value, out key)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
or similarly (to remove-by-key):
Foo value;
if(clientsByKey.TryGetValue(key, out value)) {
clientsByValue.Remove(value);
clientsByKey.Remove(key);
}
It's not very efficient to search a dictionary by it's values. However, you can use Linq to find all entries with a given value.
IEnumerable<KeyValuePair<string, myCustomClassObject>> pairs = clients
.Where(entry => entry.Value.Equals(myCustomClassObject1)).ToList();
foreach (KeyValuePair<string, myCustomClassObject> kv in pairs)
clients.Remove(kv.Key);
This should do it. It removes all clients having a given value.
while (clients.ContainsValue(myCustomClassObject1))
clients.Remove(clients.Where(x => x.Value == myCustomClassObject1).FirstOrDefault().Key);
Or create a new dictionary without the values you want removed
clients = clients.Where(x => x.Value != myCustomClassObject1).ToDictionary(k => k.Key, v => v.Value);
If the collection only contains one item with the value to be removed then you can use one of the other answers here, which will work just fine.
However, if your collection can have multiple items with the same value then you need to be careful.
You cannot modify a collection while iterating over it, so you will need to find the keys of all the items that you want to remove in one loop and put them in a list, and then iterate over that list in a separate loop to delete the items.
For example:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
var dict = new Dictionary<string, int>
{
{"Key1", 1},
{"Key2", 2},
{"Key3", 3},
{"Key4", 2},
{"Key5", 4}
};
int valueToRemove = 2;
var keysToRemove = (from element in dict
where element.Value == valueToRemove
select element.Key).ToList();
foreach (var key in keysToRemove)
dict.Remove(key);
foreach (var element in dict)
Console.WriteLine("Key = {0}, Value = {1}", element.Key, element.Value);
}
static void Main(string[] args)
{
new Program().run();
}
}
}
Use,
Following will remove only first matching value
client newClient = new client();
foreach(KeyValuePair<string, client> client in clients) {
if(client.value.equals(newClient)) {
clients.remove(client.key);
break;
}
}
Or if you want to remove all matching values,
foreach(var client in clients.Where(kvp => kvp.Value == newClient).ToList()) {
clients.Remove(client.Key);
}