I'm just learning how to program, and therefore I didn't really understand LINQ.
I have:
Dictionary<string, Dictionary<string, string>> dirData = new Dictionary<string, Dictionary<string, string>>
{
{
"key1", new Dictionary<string, string>
{
{"subKey1", "value546" },
{"subKey2", "value412" },
{"subKey3", "value100" },
{"subKey4", "value27" }
}
},
{
"key2", new Dictionary<string, string>
{
{"subKey1", "value27" },
{"subKey2", "value98" },
{"subKey3", "value100" }
}
},
{
"key3", new Dictionary<string, string>
{
{"subKey1", "value29" },
{"subKey2", "value202" },
{"subKey3", "value22" },
{"subKey5", "value1" },
{"subKey6", "value3" }
}
}
};
I need to return the Dictionary<string, Dictionary <string, string >> where subkey == "subKey3" with the value value == "value100".
How can this be organized using LINQ?
You can use the following code snippet, there are two of them in your sample BTW:
var result = dirData.Values.Where(d => d.ContainsKey("subKey3") && d["subKey3"] == "value100");
Update
I need to return the Dictionary<string, Dictionary <string, string >>
where subkey == "subKey3" with the value value == "value100".
Get Dictionary of Dictionaries:
Dictionary<string,Dictionary<string,string>> result = dirData.Where(d => d.Value.ContainsKey("subKey3") && d.Value["subKey3"] == "value100").ToDictionary(k=>k.Key,v=>v.Value);
Something along the lines of
var vals = dirData.Where(x => x.Value.Keys.Contains("subKey1") && x.Value.Values.Contains(("value29")));
should work. I just tested it using vals.Count() and got the number 1 returning.
Also, just as a heads up: there are two missing commas in your sub-dictionaries :)
Edit: I think that the answer by #Access Denied actually is probably better. Just leaving mine as an alternative.
Just complicating it a bit, you can also use Linq-object this way
var test = from x in dirData
where x.Value.Any(m => m.Key == "subKey3" && m.Value == "value100")
select x;
Here's another way:
List<Dictionary<string, string>> result = dirData.Where(w => w.Value["subKey3"] == "value100").Select(s => s.Value).ToList();
I'm presuming that more than one of the dictionaries can match the condition based on your sample data, therefore this statement returns a list of dictionaries. If you only expect one match you should replace the ToList() with Single()/SingleOrDefault() as appropriate.
probably something like
var data = dirData.Where(d => d.Value.Any(x => x.Key == "subKey3" && x.Value == "value100")).ToList();
if you are looking for only those entries where key = "subKey3" and value = "value100" then probably use SelectMany() like
var data = dirData.SelectMany(x => x.Value).Where(x => x.Key == "subKey3" && x.Value == "value100").ToList();
Related
I'm bit new to c# and want to identify keys that doesn't have same value while comparing two dictionaries.
The dictionary I have is of dict => KeyValuePair<string, string>. And I have two dictionaries like -
dict1 => {"a":"False","b":"amazonaws.com","c":"True"}
dict2 => {"a":"True","b":"amazonaws.com","c":"False"}
I want to compare both dictionaries and return a variable which will have Keys ["a", "c"] as for the above example, those keys have different value.
Currently the logic I have written will only differentiate keys that's not there in the other dictionary.
Dictionary dictExcept = null;
foreach (IDictionary kvp in dict1.Cast<object>().Where(kvp => !dict2.Contains(kvp)))
{
dictExcept.Add(kvp.Keys, kvp.Values);
}
return dictExcept ;
You can try using TryGetValue:
using System.Linq;
...
var dictExcept = dict1
.Where(pair => dict2.TryGetValue(pair.Key, out var value) &&
pair.Value != value)
.ToDictionary(pair => pair.Key,
pair => (first: pair.Value, second: dict2[pair.Key]));
Here we for each key value pair from dict1 try to get corresponding value from dict2:
// dict2 has pair.Key it corresponds to value...
dict2.TryGetValue(pair.Key, out var value) &&
// however value from dict2 != value from dict1
pair.Value != value
The very same idea if you prefer to use foreach (no Linq solution):
var dictExcept = new Dictionary<string, (string first, string second)>();
foreach (var pair in dict1)
if (dict2.TryGetValue(pair.Key, out var value) && value != pair.Value)
dictExcept.Add(pair.Key, (pair.Value, value));
Demo: (fiddle)
var dict1 = new Dictionary<string, string> {
{ "a", "False" }, { "b", "False" }, { "c", "True" }, { "d", "dict1 only" } };
var dict2 = new Dictionary<string, string> {
{ "a", "False" }, { "b", "True" }, { "c", "False" }, { "e", "dict2 only" } };
var dictExcept = dict1
.Where(pair => dict2.TryGetValue(pair.Key, out var value) &&
pair.Value != value)
.ToDictionary(pair => pair.Key,
pair => (first: pair.Value, second: dict2[pair.Key]));
string report = string.Join(Environment.NewLine, dictExcept
.Select(pair => $"Key: {pair.Key}; Values: {pair.Value}"));
Console.Write(report);
Outcome:
Key: b; Values: (False, True)
Key: c; Values: (True, False)
Providing the simplest answer because of your comment:
Both the dictionaries will have same keys, just that we need to identify keys that has different values
Working under that assumption that you don't need to account for missing keys, you can simply iterate over all the keys of one of the dictionaries, and compare the values found under that key.
var keysWithDifferentValues = new List<string>();
foreach (var kvp in dict1)
{
if(!kvp.Value.Equals(dict2[kvp.Key]))
keysWithDifferentValues.Add(kvp.Key);
}
This can be simplified using LINQ:
var keysWithDifferentValues =
dict1
.Where(kvp => !kvp.Value.Equals(dict2[kvp.Key]))
.Select(kvp => kvp.Key)
.ToList();
Since you have a dictionary named dictExcept, what about using Expect to do the job for you?
Produces the set difference of two sequences.
source
And in your case:
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
static void Main(string[] args)
{
var a = new Dictionary<string, string>{{"a", "False"}, {"b", "False"}, {"c", "True"}};
var b = new Dictionary<string, string>{{"a", "False"}, {"b", "True"}, {"c", "False"}};
var c = a.Except(b).Select(x => x.Key);
c.Dump();
}
}
output
[ b, c ]
Try it online!
More examples with different cases:
static void Main(string[] args)
{
var a = new Dictionary<string, string>{{"a", "False"}, {"b", "False"}, {"c", "True"}};
var b = new Dictionary<string, string>{{"a", "False"}, {"b", "True"}, {"c", "False"}};
var c = a.Except(b).Select(x => x.Key);
// c is [ b ,c ]
a.Add("d", "foo");
var d = a.Except(b).Select(x => x.Key);
// d is [ b, c, d ]
b.Add("e", "foo");
var e = a.Except(b).Select(x => x.Key);
// e is still [ b, c, d ]
var e2 = (a.Except(b)).Union(b.Except(a)).Select(x => x.Key).Distinct();
// e is [ b, c, d, e ]
}
Try it Online!
I have common subkeys (SubKey1/SubKey2, etc) for each set of BaseKey1, BaseKey2, .... etc, but occurrence of all subkeys for each basekey is NOT fixed. In below example "SubKey2" not present for "BaseKey2".
var model = new Dictionary<string, Dictionary<string, Model>>
{
{
"BaseKey1",
new Dictionary<string, Model>
{
{"SubKey1", new Model { Time = new DateTime(2020, 09, 15)}},
{"SubKey2", new Model { Time = new DateTime(2020, 12, 15) }}
}
},
{
"BaseKey2",
new Dictionary<string, Model>
{
{"SubKey1", new Model { Time = new DateTime(2020, 11, 15) }},
}
}
};
I need to pull Min Time for each subkey from all basekey and to do so I am doing below,
var query = model.Values
.SelectMany(d => d.Keys)
.Distinct()
.Select(key => new
{
Key = key,
Time = model.Values.Min(v => v[key].Time)
})
.ToList();
But it's giving error "The given key was not present in the dictionary" due to "SubKey2" not present for "BaseKey2". What could be solution here? I need below output. Thanks!
You're not going to be able to do this without some loops -- the fact that you're using a dictionary is more or less irrelevant.
My understanding is that you want something like this:
var result = new Dictionary<string, Model>();
// For each "BaseKey" dictionary...
foreach (var subDictionary in model.Values)
{
// For each "SubKey" and its corresponding value
foreach (var (key, value) in subDictionary)
{
// If we haven't yet recorded a value for this SubKey, add this value
// If we have, but it's higher than the value for this SubKey, replace it
if (!result.TryGetValue(key, out var existingValue) || value.Time < existingValue.Time)
{
result[key] = value;
}
}
}
See it working here.
You can sprinkle in a bit of LINQ to remove one of the loops quite easily:
var result = new Dictionary<string, Model>();
foreach (var (key, value) in model.Values.SelectMany(x => x))
{
if (!result.TryGetValue(key, out var existingValue) || value.Time < existingValue.Time)
{
result[key] = value;
}
}
See it working here.
If you really want to LINQ it up, you'll want something like:
var result = model.Values
.SelectMany(x => x)
.GroupBy(x => x.Key)
.ToDictionary(
x => x.Key,
x => x.Select(m => m.Value)
.MinBy(m => m.Time));
... where MinBy is provided by e.g. this answer. That's probably going to be measurably slower though, and isn't any shorter.
See it working here.
Check this
Dictionary<string, Model> flatSubDict = new Dictionary<string, Model>();
foreach(var key in model.Keys)
{
foreach(var mKey in model[key].Keys)
{
if(flatSubDict.TryGetValue(mKey, out var m))
{
// if existing time greater than new time then replace
if(m.Time > model[key][mKey].Time)
{
flatSubDict[mKey] = m;
}
}
else
flatSubDict[mKey] = m;
}
}
return flatSubDict.Values;
I have the following string:
string x = "23;32;323;34;45";
and I want to replace 23 with X as below:
x = "x:32;323;34;45";
but when I try it, I get this instead:
x = "x:32;3x;34;45";
Is there a way I can get the expecte output?
You will need a regular expression (regexp). The replacement rule here is
word boundary
23
word boundary
so your code would look like this
var result = Regex.Replace(input, #"\b23\b", "X");
An alternative approach would be to split your string, replace matching elements and join to new string>
var result = string.Join(";", input.Split(";").Select(v => v == "23" ? "X" : v));
Update: Update value in Dictionary
Assuming you know the key, that's easy:
myDict["thekey"] = Regex.Replace(myDict["thekey"], #"\b23\b", "X");
If you want to do this replacement for all items, I'd do it like this, but I'm not sure, if this is the best possible solution:
[Fact]
public void Replace_value_in_dict()
{
// given
var mydict = new Dictionary<string, string>
{
{ "key1", "donothing" },
{ "key2", "23;32;323;34;45" },
};
// when
var result = mydict
.Select(kv => (kv.Key, Regex.Replace(kv.Value, #"\b23\b", "X")))
.ToDictionary(x => x.Item1, x => x.Item2);
// then
Assert.Equal(result, new Dictionary<string, string>
{
{ "key1", "donothing" },
{ "key2", "X;32;323;34;45" },
});
}
You should use regex
var x="23;32;323;34;45";
var res = Regex.Replace(x, #"\b23\b", "x");
Console.WriteLine(res);
Working sample
I need to replace all placeholders like {text} with a corresponding value from a dictionary.
This is my code:
var args = new Dictionary<string, string> {
{"text1", "name"},
{"text2", "Franco"}
};
saveText(Regex.Replace("Hi, my {text1} is {text2}.", #"\{(\w+)\}", m => args[m.Groups[1].Value]));
The problem is: if the text in the input string does not exist in the dictionary, it throws an exception but I rather need to replace the placeholder with the string "null".
Just expand your lambda:
var args = new Dictionary<string, string> {
{"text1", "name"},
{"text2", "Franco"}
};
saveText(Regex.Replace("Hi, my {text1} is {text2}.", #"\{(\w+)\}", m => {
string value;
return args.TryGetValue(m.Groups[1].Value, out value) ? value : "null";
}));
I would use LINQ to create a single Func<string, string> that performs all of the replacements in one go.
Here's how:
var replace = new Dictionary<string, string>
{
{ "text1", "name" },
{ "text2", "Franco" }
}
.Select(kvp => (Func<string, string>)
(x => x.Replace(String.Format("{{{0}}}", kvp.Key), kvp.Value)))
.Aggregate<Func<string, string>, Func<string, string>>(
x => Regex.Replace(x, #"\{(\w+)\}", "null"),
(a, f) => x => a(f(x)));
var result = replace( "Hi, my {text1} is {text2} and {text3} and {text4}.");
// result == "Hi, my name is Franco and null and null."
I have a dictionary defined as seen here:
Dictionary<int, Dictionary<string, object>> dict = new Dictionary<..>();
And a sample code for adding data:
dict.Add (X, new Dictionary<string, object> ());
dict [X].Add ("Car", CarObject);
dict [X].Add ("Seller", SellerObject);
dict [X].Add ("Key3", Z);
I want to search in the inner dictionary if it contains an object which then contains the following
CarObject.Name = (wildcard)X1(wildcard) of the key "Car" but I just can't seem to get a grasp of how to get into the inner dictionary and then into the object with LINQ in order to search for the value.
This will return all matching KeyValuePair<string, object>.
var query = dict.SelectMany(d => d.Value)
.Where(i => i.Key == "Key1"
&& (
i.Value is CarObject
? ((CarObject)i.Value).Name.Contains("X1")
: false
));
Try the following:
var results = dict[X].Where(x => (x.Value is CarObject) && ((CarObject)x.Value).Name.Contains("X1"));
If you want to get just the value and not the dictionary, and print the values, you can do this:
int X = 0, Z = 1;
dict[X].Add("Key1", CarObject);
dict[X].Add("Key2", SellerObject);
dict[X].Add("Key3", Z);
var results = dict[X].Where(x => (x.Value is CarObject) && ((CarObject)x.Value).Name.Contains("X1")).Select(x => x.Value);
foreach (var str in results)
Console.WriteLine(str);
You can try something like:
dict[X].Where(x => x.Value is CarObject && ((CarObject)x.Value).Name.Contains("X1"));
Or:
dict[X].Values.OfType<CarObject>().Any(x => x.Name.Contains("X1"))