Merging two dictionary pairs c# not pure concat - c#

I have two Dictionary.
Dictionary<string, string> testDict = new Dictionary<string, string>();
testDict.Add("Name", "John");
testDict.Add("City", "NY");
Dictionary<string, string> DictA = new Dictionary<string, string>();
DictA.Add("Name", "Sarah");
DictA.Add("State", "ON");
I wish to get a Dictionary such that the keys of testDict are present and the values of those keys present in DictA are present.
So the example merged dictionary should look as below:
Dictionary<string, string> DictMerged = new Dictionary<string, string>();
DictMerged.Add("Name", "Sarah");
DictMerged.Add("City", "NY");
I hope I have been able to explain my requirements..
I tried..
testDict.Concat(DictA)
.GroupBy(kvp => kvp.Key, kvp => kvp.Value)
.ToDictionary(g => g.Key, g => g.Last());
But this gave me DictA 'State' as well which I do not want..
Any help is sincerely appreciated
Thanks

I think you are looking for this:
var result =
testDict.ToDictionary(
i => i.Key,
i => DictA.ContainsKey(i.Key) ? DictA[i.Key] : i.Value);
// result:
// {"Name", "Sarah"}
// {"City", "NY"}

If you have the GetOrDefault extension method defined here
Then you can do
var result = testDict.ToDictionary(
kvp => kvp.Key,
kvp => DictA.GetOrDefault(kvp.Key, kvp.Value));
The difference between this and using DictA.ContainsKey(kvp.Key) ? DictA[kvp.Key] : i.Value is that there is only one lookup done on DictA versus two when the key is present.

This would work
var testDict = new Dictionary<string, string> { { "Name", "John" }, { "City", "NY" } };
var DictA = new Dictionary<string, string> { { "Name", "Sarah" }, { "State", "ON" } };
var mergedDict = testDict.ToDictionary(keyVal => keyVal.Key, keyVal => DictA.ContainsKey(keyVal.Key) ? DictA[keyVal.Key] : keyVal.Value);

Related

Linq Query Dictionary where value not in List

I have a Dictionary and another List. What I am trying to achieve is a LINQ query to get all items out of the dictionary where any values from said dictionary are not in the List<string>.
I found this post to be helpful, Linq Query Dictionary where value in List. And was able to write the following LINQ expression.
What I have so far:
Data is the dictionary and PersonList is the list of strings.
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
However, my results never actually return anything.
Update:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Key))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
Is it possible that you made a typo here and meant to write this instead?
// changed to kvp.Value from kvp.Key
var Persons = Data.Where(kvp => !PersonList.Contains(kvp.Value))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
This searches the values from the dictionary in the list and retains those entries which are not in the list, as per your question.
Answering this myself as I've been looking into the problem from wrong angle.
Following works as expected:
Dictionary<string, string> Data = new Dictionary<string, string>
{
{ "John", "aaa" },
{ "Tom", "bbb" },
{ "David", "ccc" }
};
List<string> PersonList = new List<string>
{
"Tom",
"Peter"
};
List<string> PersonListNotInDictionary = PersonList.Where(pl => !Data.ContainsKey(pl))
.ToList();
It is possible that the contains doesn't work, because the items in the PersonList are other objects than the keys in your dictionary. The objects might be the same (have the same content), but if it are different object (different refences), than the contains will return false.
An example:
myObject = new myObject() { Id = 1 };
List<myObject> listOfObjects = new List<myObject>();
listOfObjects.Add(new myObject() { Id = 1 });
var result = listOfObjects.Contains(myObject); // returns false, because the item in the list is a different object than myObject
result = listOfObjects.Any(obj => obj.Id == myObject.Id); // returns true
I don't know what the PersonList consist of, but if the elements have, for instance, an Id property than you could do something like this:
var Persons = Data.Where(kvp => !PersonList.Any(person => person.Id == kvp.Key.Id))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
You can try following code snippet
var result = data.Where(d => PersonList.All(p => p != d.Value));

Convert List of Dictionary<string, List> into list C# Linq

This is my source type => IEnumerable<IDictionary<string, IEnumerable<string>>>
This is my target type => IEnumerable<string>
Expected Output
List of strings
[
"ca:aws_client:firstElementIntheList]",
"ca:aws_client:secondElementInTheList]"
]
Actual Output
List of strings
[
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]",
"ca:aws_client:System.Linq.Enumerable+SelectListIterator`2[System.String,System.String]"
]
Code
input
.ToList()
.SelectMany(d => d)
.Select(i => $"ca:{i.Key}:{i.Value.Select(l => l)}")
.ToList()
You're looking to use a result selector in SelectMany instead of a second select statement.
Something like this may be what you want:
var dict = new Dictionary<string, IEnumerable<string>>
{
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
};
var res = dict.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
/* output:
ca:one:1
ca:one:2
ca:one:3
ca:two:1
ca:two:2
ca:two:3
*/
Edit:
I've noticed you're actually using a List of Dictionaries. The solution is largely the same, but with more SelectMany.
var list = new List<Dictionary<string, IEnumerable<string>>>
{
new Dictionary<string, IEnumerable<string>> {
{"one", new List<string> {"1","2","3"}},
{"two", new List<string> {"1","2","3"}}
},
new Dictionary<string, IEnumerable<string>> {
{"three", new List<string> {"1","2","3"}},
{"four", new List<string> {"1","2","3"}}
}
};
var res = list.SelectMany(x => x)
.SelectMany(d => d.Value, (a, b) => $"ca:{a.Key}:{b}");
foreach(var val in res)
Console.WriteLine(val);
If someone can suggest a clean way to handle the multiple method syntax SelectMany other than stacking them, I'd like to know for my own edification.

How to use LINQ to find a sum?

I have this structure:
private readonly Dictionary<string, Dictionary<string, int>> _storage =
new Dictionary<string, Dictionary<string, int>>();
key: Firmware(string): key: Device(string) : value CountOfUsers (int)
I need to get the total of users for each device, but I really don't know how to do it with LINQ. Already tried a lot of variants. Please, help!
For now, I just use a whole function for it
private XlsRow2 GetTotalPerDevice(Dictionary<string, Dictionary<string, int>> storage)
{
XlsRow2 totalPerDeviceRow = new XlsRow2();
totalPerDeviceRow._Name = "Grand Total";
totalPerDeviceRow.UseBorders = true;
foreach (var deviceModel in _allDeviceModels)
{
foreach (var firmware in storage)
{
foreach (var device in firmware.Value)
{
var countOfUsers = 0;
if (deviceModel == device.Key)
{
countOfUsers += device.Value;
if (!_totalsPerDevice.ContainsKey(deviceModel))
{
_totalsPerDevice.Add(deviceModel, countOfUsers);
}
else
{
_totalsPerDevice[deviceModel] += countOfUsers;
}
}
}
}
}
foreach (var deviceModel in _allDeviceModels)
{
if (_totalsPerDevice.ContainsKey(deviceModel))
{
totalPerDeviceRow._AddColumn(_totalsPerDevice.First(k => k.Key == deviceModel.ToString()).Value.ToString());
}
else
{
totalPerDeviceRow._AddColumn("");
}
}
return totalPerDeviceRow;
}
Something like this for example?
var result = _storage.SelectMany(x => x.Value)
.GroupBy(x => x.Key)
.Select(x => new { Device = x.Key, Total = x.Sum(y => y.Value) });
Since the keys for the data that you would like to aggregate is in the second-level dictionary, a good first step would be to dump all key-value pairs from inner dictionaries into a flat sequence. After that all you need is to aggregate the counts, like this:
var res = _storage
.SelectMany(d => d.Value)
.GroupBy(kvp => kvp.Key)
.ToDictionary(g => g.Key, g => g.Sum(kvp => kvp.Value));
A Dictionary implements IEnumerable<KeyValuePair<TKey,TValue> which means you can use LINQ on it. In this case you have a dictionary of dictionaries and need to group by the second level key. To do that, you need to flatten the dictionaries, something that can be done with SelectMany
_storage.Selectmany(pair=>pair.Value);
Once you have the leaf-level entries, you can group by their keys:
_storage.Selectmany(pair=>pair.Value)
.GroupBy(leaf=>leaf.Key);
And calculate the sum per group:
var totals=_storage.SelectMany(pair=>pair.Value)
.GroupBy(leaf=>leaf.Key)
.Select(grp=>new {
Device = grp.Key,
TotalUsers =grp.Sum(leaf=>leaf.Value)
});
The equivalent query is rather cleaner:
var totals2 = from frm in _storage
from dev in frm.Value
group dev by dev.Key into grp
select new {
Device = grp.Key,
Total=grp.Sum(leaf=>leaf.Value)
};
Given the following dictionary:
var _storage = new Dictionary<string, Dictionary<string, int>> {
["Frm1"]=new Dictionary<string, int> {
["Device1"]=4,
["Device2"]=5
},
["Frm2"]=new Dictionary<string, int> {
["Device1"]=41,
["Device3"]=5
}
};
Both queries return the same values
foreach(var total in totals)
{
Console.WriteLine ($"{total.Device} = {total.Total}");
}
------------------
Device1 = 45
Device2 = 5
Device3 = 5
You can do this like:
Dictionary<string, Dictionary<string, int>> _storage = new Dictionary<string, Dictionary<string, int>>();
Dictionary<string, int> x = new Dictionary<string, int>();
x.Add("x", 2);
x.Add("z", 2);
x.Add("y", 2);
_storage.Add("x", x);
_storage.Add("z", x);
_storage.Add("y", x);
var b = _storage.SelectMany(keyValuePair => keyValuePair.Value)
.GroupBy(keyValuePair => keyValuePair.Key)
.ToDictionary(valuePairs => valuePairs.Key, grouping => grouping.Sum(kvp => kvp.Value));
result will be like:

Replace String with Dictionary c#

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."

parsing string and create Dictionary

I have the following string pattern: 1:2,2:3.
This is like array in one string:
The first element is: 1:2
The second element is: 2:3
I want to parse it and create a dictionary:
1,2 // 0 element in Dictionary
2,3 // 1 element in Dictionary
This is my code:
Dictionary<int,int> placesTypes = new Dictionary<int, int>();
foreach (var place in places.Split(','))
{
var keyValuePair = place.Split(':');
placesTypes.Add(int.Parse(keyValuePair[0]), int.Parse(keyValuePair[1]));
}
Is there the best way to do this?
Thanks.
You could change it to this:
var d = s.Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0]), x => int.Parse(x[1]));
Dictionary<int, int> dict = "1:2,2:3".Split(',')
.Select(x => x.Split(':'))
.ToDictionary(x => int.Parse(x[0]),
x => int.Parse(x[1]));
var result = input.Split(new[]{','})
.Select(s => s.Split(new[]{':'}))
.ToDictionary(k => int.Parse(k[0]), v=> int.Parse(v[1]));
Live example: http://rextester.com/GTKO60478
If you're using C# >= 3.5 the ToDictionary LINQ method is the way to go:
var dictionary = places.Split(',')
.Select(place => place.Split(':'))
.ToDictionary(keyValue => int.Parse(keyValue[0]), keyValue => int.Parse(keyValue[1]));
Failing that, the following should suffice:
public static Dictionary<string, string> ToDictionary(string value, char pairSeperator, char valueSeperator)
{
Dictionary<int, int> dictionary = new Dictionary<int, int>();
foreach (string pair in value.Split(pairSeperator))
{
string[] keyValue = pair.Split(valueSeperator);
dictionary.Add(keyValue[0], keyValue[1]);
}
return dictionary;
}
Splitting only once, using MoreLinq.Batch
Dictionary<int, int> dict = places.Split(',', ':').Batch(2).Select(x=>x.ToArray()).ToDictionary(x=>int.Parse(x[0]),x=>int.Parse(x[1]));

Categories