I have List<Dictionary<string, string>> object with some datas in it.
/* Values in the list will be like
[0] -
aaa - aaaValue1 (Key, Value)
bbb - bbbValue1
ccc - cccValue1
ddd - dddValue1
[1] -
aaa - aaaValue2 (Key, Value)
bbb - bbbValue2
ccc - cccValue2
ddd - dddValue2
and so on */
I want to get the distinct values( List<string> ) in the dictionary where the key is equal to "ccc" and the value of the key "bbb" is equal to "bbbValue1".
Expected Result:
Return a string list contains the dictionary value where key is equal to "ccc" and the value of the key "bbb" is equal to "bbbValue1" in the List<Dictionary<string, string>>.
I think you want:
var result = testData.Where(dict => dict.ContainsKey("EmpNo"))
.Select(dict => dict["EmpNo"])
.Distinct()
.ToList();
or if you want the result as a set:
var result = new HashSet<string>(from dict in testData
where dict.ContainsKey("EmpNo")
select dict["EmpNo"]);
EDIT:
You've changed your question completely, which isn't a nice thing to do (ask a new one instead), but to answer it in its current state:
var result = testData.Where(dict => dict.ContainsKey("ccc")
&& dict.ContainsKey("bbb")
&& dict["bbb"] == "bbbValue1")
.Select(dict => dict["ccc"])
.Distinct()
.ToList()
Think it will be better to flatten list like this:
testData.SelectMany(x => x)
.Where(x => x.Key == "EmpNo")
.Select(x => x.Value)
.Distinct()
.ToList();
I think this will give you the correct result:
var result = testData.SelectMany(dict => dict)
.Where(dict => dict.Key.Equals("ccc") || (d.Key.Equals("bbb") && d.Value.Equals("bbbValue1")))
.Select(d => d.Value).Distinct().ToList();
Related
Looking for someone to help me with C#, LINQ.
I have a Dictionary<int,int> that I am ordering like so: .OrderBy(_ => _.Value).ThenBy(_ => ThisMethodReturnsAnotherIReadOnlyDict<int,int>()).ThenByDescending(_ => _.Key).
What I want is to order the first dictionary by its value and then if there are still equal values I want that tie to be broke by the ThisMethodReturnsAnotherIReadOnlyDict<int,int>(). This first key/value of this ThisMethodReturnsAnotherIReadOnlyDict to break the tie and be on top. And finally, if everything fails, then order by it's key descending.
Some data for this like:
(First Dictionary)
[1,400]
[2,550]
[3,200]
[4,200]
(Second dictionary)
[3,50]
[4,140]
[2,600]
[1,700]
For this scenario I want my ordering to return: [3,50]
Can anyone help please?
Thanks!
It looks like you're looking for something like this:
var firstDict = new Dictionary<int, int>() {
{1,400},
{2,550},
{3,200},
{4,200}
};
var secondDict = new Dictionary<int, int>() {
{3,50},
{4,140},
{2,600},
{1,700}
};
var result = (from kvp in firstDict
join tieBreaker in secondDict on kvp.Key equals tieBreaker.Key
select new { kvp.Key, V1 = kvp.Value, V2 = tieBreaker.Value })
.OrderBy(x => x.V1)
.ThenBy(x => x.V2)
.ThenByDescending(x => x.Key)
.First();
This will join the first and second dictionaries together by its keys and will, respectively, order by the value of the first dictionary, the value of the second dictionary and then descending by the key itself.
How about:
var ans = firstDict
.OrderBy(kv => kv.Value)
.ThenBy(kv => ThisMethodReturnsAnotherIReadOnlyDict<int,int>().TryGetValue(kv.Key, out var val2) ? val2 : kv.Key)
.ToList();
Unless ThisMethodReturnsAnotherIReadOnlyDict<int,int> may change, you may want to cache the return value in a variable before sorting.
var aaa = data.GroupBy(o => o.Date).Select(o => new { o }).ToList();
var bbb = aaa.Select(o => o.Key).ToList();
//There is a error of `Key`
Does that mean the Key is only allowed for the original List after GroupBy. Is it possible to obtain the Key for any Select after GroupBy?(Surely, we can storge the Key = o.Key in the Select )
Furthermore,
var aaa = data.GroupBy(o => o.Date).Select(o => o.ToList()}).ToList();
If we change aaa into two dimensional List, Is it possible to obtain the previous Key?
In your first linq expression, in the Select you are wrapping the IGrouping object that you got from the GroupBy with a new anonymous object.
So to get that Key property in your second line you should:
//Original:
var bbb = aaa.Select(o => o.Key).ToList();
//Change to:
var bbb = aaa.Select(o => o.o.Key).ToList();
For second question, if you want to get the Key in this case:
//Original:
var aaa = data.GroupBy(o => o.Date)
.Select(o => o.ToList()})
.ToList();
//Then you should:
var aaa = data.GroupBy(o => o.Date)
.Select(o => o.ToList()})
.Select(x => o.FirstOrDefault().Date)
.ToList();
Reason being is that:
You group your items by Date
First select you convert a IGrouping into a List<YourClass> but now you have an IEnumerable<List<YourClass>> where each record in the IEnumerable, all the inner items will have the same date
In second Select - take whichever item in the inner collections - and get the Date it is the same as getting the Key in the example before
To achieve what you actually what to get (grouping by the date and getting for each group the symbols):
var result = data.GroupBy(item => item.Date)
.Select(group => new { group.Key, Symbols = group.Select(item => item.Symbol).ToList() });
//Or using a different overload of the `GroupBy`:
var result = data.GroupBy(item => item.Date,
(key,group) => return new { Key = key, Symbols = group.Select(item => item.Symbol).ToList() });
Problem is your incorrect syntax. You crated an anonymous object, so you need to access the property with instance name. So you should be doing this.
var bbb = aaa.Select(o => o.o.Key).ToList();
if we change aaa into two dimensional List, Is it possible to obtain
the previous Key?
No, because you have groped values collection not the Key. So result will not contain Key.
I have the following dictionary:
Dictionary<string, string> clauses = new Dictionary<string, string>();
where the clauses are like this:
"A|B" - "some text"
"A|D|E" - "some text"
"G" - "some text"
"E|A" - "some text"
...
and I want to populate the dictionary below:
Dictionary<string, int> columnsBitMap = new Dictionary<string, int>();
where the string values are the unique letters of the first dictionary strings and int values are calculated by math formula.
I have the following which is working perfectly:
columnsBitMap = String.Join("|", clauses.Select(clause => clause).Select(clause => clause.Key)).Split('|')
.Distinct().OrderBy(column => column)
-- can I remove the next Select ?
.Select((column, index) => new KeyValuePair<string, int>(column, index))
.ToDictionary(column => column.Key, column => Convert.ToInt32(Math.Pow(2, column.Value)));
but I am wondering if this could be simplified removing the .Select part?
The output should be like this:
A 1
B 2
D 4
E 8
G 16
This bit is completely superfluous:
.Select(clause => clause)
Just remove it and the rest should work fine.
I dont see much reason to get rid of the part
.Select((column, index) => new KeyValuePair<string, int>(column, index))
But if you're against using a KeyValuePair<TKey,TValue> you could just make it an anonymous object
.Select((column, index) => new{ Key = column, Value = index })
But there's not a great amount of difference.
I approached your requirement in a slightly different way:
var result = clauses.SelectMany(clause => clause.Key.Split('|'))
.Distinct().OrderBy(column => column)
.Select((column, index) => new {Key=column,Value=index})
.ToDictionary(column => column.Key, column => Convert.ToInt32(Math.Pow(2, column.Value)));
Working example with your test case: http://rextester.com/PWC41147
How to concatenate and remove duplicates in dictionary like this:
Item1: Key=1, Value=test1
Item2: Key=2, Value=test2
Item3: Key=3, Value=test1
Item4: Key=4, Value=test3
Item5: Key=5, Value=test4
To build a new Dictionary like this:
Item1: Key=1-3, Value=test1
Item2: Key=2, Value=test2
Item4: Key=4, Value=test3
Item5: Key=5, Value=test4
I managed to get the duplicates using this: myDictionary.GroupBy(x => x.Value).Where(x => x.Count() > 1);
But I can't figure the right way to build a new Dictionary from this.
This works for taking all the duplicate items into a new dictionary with a shared key:
var dict = new Dictionary<string, string>
{
{"1", "test1"},
{"2", "test2"},
{"3", "test1"}
};
var groupedKeyMap = dict.GroupBy(x => x.Value)
.Where(x => x.Count() > 1)
.ToDictionary(x => string.Join("-", x.Select(y => y.Key)),
x => x.Key);
If you need both duplicate and non duplicate key value pairs, remove the Where clause:
var groupedKeyMap = dict.GroupBy(x => x.Value)
.ToDictionary(x => string.Join("-", x.Select(y => y.Key)),
x => x.Key);
public static IEnumerable<KeyValuePair<string, string>> GetGroupKeyValuePairs(string category)
{
var list = new List<KeyValuePair<string, string>>();
using (DataConnection connection = new DataConnection())
{
List<KeyValuePair<string,string>> settings = connection.Get<Settings>()
.Where(a => a.Category == category )
.Select(pair => new KeyValuePair<string,string>(pair.TheName, pair.TheValue))
.ToList();
list = settings;
}
return list;
}
The exception is:
InvalidOperationException:
Key 'Garanti.Oda' appears more than one time
How can I collect duplicate keys?
The method that you show isn't going to have a problem with multiple pairs with the same key. I assume that afterward, you're doing something like creating a dictionary of these pairs, and that's where you have a problem. E.g.
var pairs = GetGroupKeyValuePairs("some category");
var dict = new Dictionary<string, string>();
foreach (var pair in pairs)
dict.Add(pair.Key, pair.Value); // exception when it hits a duplicate
Instead, you need to use the pairs in a way that's friendly to duplicates, e.g. ToLookup.
var pairs = GetGroupKeyValuePairs("some category");
var lookup = pairs.ToLookup(x => x.Key, x => x.Value);
Then, for example if the list had "a", "b" and "a", "c", then lookup["a"] gives you "b" and "c".
Assuming you want to find duplicates by Key only (e.g. so that you can build a dictionary), you could GroupBy the prospective key and find all instances of more than one:
var dupeSettings = connection.Get<Settings>()
.Where(a => a.Category == category)
.GroupBy(a => a.TheName)
.Where(grp => grp.Count() > 1)
.Select(dupe => dupe.Key)
.ToList();
Or, if you want duplicates of both key and value, project and group by an anonymous class:
var dupeSettings = connection.Get<Settings>()
.Where(a => a.Category == category)
.GroupBy(a => new {a.TheName, a.TheValue})
.Where(grp => grp.Count() > 1)
.Select(dupe => dupe.Key) // Key.TheName, Key.TheValue
.ToList();