I have two dictionaries in c#.
The Two Dictionaries and their calues are
Dictionary<int,List<string>> D1 = new Dictionary<int,List<string>>();
Dictionary<int,List<string>> D2= new Dictionary<int,List<string>>();
Dictionary<int,List<string>> D3 new Dictionary<int,List<string>>();
D1[1] = new List<string>{"a","b"};
D1[2] = new List<string>{"c","d"};
D1[3] = new List<string>{"e","f"};
D1[4] = new List<string>{"h"};
Where 1,2,3 and 4 are keys of Dictionary D1
D2[1] = new List<string>{"a","b"};
D2[2] = new List<string>{"c","d"};
D2[3] = new List<string>{"e","f"};
D2[4] = new List<string>{"g"};
D2[5] = new List<string>{"b","h"};
D2[6] = new List<string>{"f","l"};
D2[7] = new List<string>{"z"};
Where 1,2,3,4,5,6 and 7 are keys of Dictionary D2
Then the output Dictionary Contains this values,
D3[1] = {"a","b","h"} D3[2] = {"c","d"} D3[3] = {"e","f","l"}
Note: Please take the Input Dictionary with values greater than 1.Thats why i am eliminating the D1[4] , D2[4] and D2[7]
IS IT POSSIBLE TO MERGE IT USING LINQ?
Yes it's possible but it's not pretty!
//firstly lets get the keys that are valid (i.e. have more than one element in their list)
var validD1Elements = D1.Where(d => d.Value.Count > 1);
var validD2Elements = D2.Where(d => d.Value.Count > 1);
//merge the valid keys together so we know which ones we want to select
var mergedKeys = validD1Elements.Select(d => d.Key).Union(validD2Elements.Select(d => d.Key));
//perform the merge
var mergeResult = mergedKeys.Select (key => new
{
Key = key,
//select the values from D1
Value = validD1Elements.Where(d => d.Key == key).SelectMany(d => d.Value)
//concat the values from D2
.Concat(validD2Elements.Where(d => d.Key == key).SelectMany(d => d.Value))
}).ToDictionary(e => e.Key, e => e.Value);
This merge uses Concat so you will get duplicates, i.e. mergeResult[1] will be { "a", "b", "a", "b" }.
If you do not want duplicates change the following code from this:
//concat the values from D2
.Concat(validD2Elements.Where(d => d.Key == key).SelectMany(d => d.Value))
to this:
//union the values from D2
.Union(validD2Elements.Where(d => d.Key == key).SelectMany(d => d.Value))
mergeResult[1] will then be { "a", "b" }.
Concat them all, then group by (ToLookup) the key, then union all the values in a grouping, finally shove them all back in a dictionary.
Related
I have two dictionaries of the same type, A and B.
Dictionary<string, IEnumerable<object>>
I'm using object to represent a complex type having a property 'Id'.
I'm looking for all items in A having objects that exist in B (using Id), but under a different key. It's basically to tell if an object has moved keys. A is the new dictionary and B is the old.
Is there a reasonable way to accomplish this using LINQ? I would like the result to be a dictionary of all key-value pairs in A meeting the criteria. Thanks in advance.
I use Interface IHasId for use Id propert:
public interface IHasId
{
int Id { get; }
}
And class AAA that inherited the interface:
public class AAA: IHasId
{
public int Id { get; set; }
}
Here the linq you look for:
Dictionary<string, IEnumerable<IHasId>> A = new Dictionary<string, IEnumerable<IHasId>>();
A.Add("111", new List<IHasId> { new AAA { Id = 1 }, new AAA { Id = 2 } });
A.Add("333", new List<IHasId> { new AAA { Id = 3 } });
Dictionary<string, IEnumerable<IHasId>> B = new Dictionary<string, IEnumerable<IHasId>>();
B.Add("111", new List<IHasId> { new AAA { Id = 1 }});
B.Add("222", new List<IHasId> { new AAA { Id = 2 }});
B.Add("333", new List<IHasId> { new AAA { Id = 3 } });
var res = A.Where(a => a.Value.Any(c => B.Any(v => v.Value
.Select(x => x.Id).Contains(c.Id) && a.Key != v.Key))).ToList();
In this example it return key 111 that has the object with Id = 2 that moved from key 222 to key 111
If you want the result as dictionary you can change the ToList with ToDictionary:
var res = A.Where(a => a.Value.Any(c => B.Any(v => v.Value
.Select(x => x.Id).Contains(c.Id) && a.Key != v.Key)))
.ToDictionary(a=>a.Key, a=>a.Value);
If you want in the new dictionary only the values that has change, like in the example key 111 and value with only the object with Id = 2, you can do it like this:
var res = A.Select(a => new KeyValuePair<string, IEnumerable<IHasId>>(a.Key,
a.Value.Where(c => B.Any(v => v.Value.Select(x => x.Id).Contains(c.Id) && a.Key != v.Key))))
.Where(a=>a.Value.Count() > 0)
.ToDictionary(a => a.Key, a => a.Value);
In terms of searchability, your dictionary has it backwards; it is efficient for looking up an object given a string, but you need to be able to look up the strings for a given object. An efficient data structure for this purpose would be a Lookup<object,string>.
First, use ToLookup() to create a lookup table where the key is the object and the value is the list of keys in both list A and B. Use Union (instead of Concat) to eliminate duplicates.
var lookup = listA
.Union( listB )
.ToLookup( pair => pair.Value, pair => pair.Key );
Once you have the lookup, the problem is trivial.
var results = lookup.Where( x => x.Count() > 1);
See this DotNetFiddle for a working example with sample data.
If you need A entries with original objects, it could be:
var result = A.Where(a => B.Any(b => b.Key != a.Key && b.Value.Intersect(a.Value).Any()));
If you need A entries with only matching objects from B, it could be:
var result = A.Select(a => new KeyValuePair<string, IEnumerable<object>>(a.Key, B.Where(b => b.Key != a.Key).SelectMany(b => b.Value.Intersect(a.Value)))).Where(x => x.Value.Any());
You can provide a custom equality comparer for Intersect to match items by Id or whatever.
Use new Dictionary<string, IEnumerable<object>>(result) if you need it as a dictionary.
Use the Join operator (see join clause (C# Reference)):
var dictionary = (
from a in (from entry in A from Value in entry.Value select new { entry.Key, Value })
join b in (from entry in B from Value in entry.Value select new { entry.Key, Value })
on ((dynamic)a.Value).Id equals ((dynamic)b.Value).Id
where a.Key != b.Key
select a
).ToDictionary(a => a.Key, a => a.Value);
I have a list of string:
var list = new List<string>();
Data in the list is in this format A1_A2_A3, A1_A2_A3, A1_A2_A3
I want to split each element A1_A2_A3 of list by _ and store the result into List<Item>
Where Item is a class containing three properties a1, a2, a3
I want to do it using LINQ, I can do this by using loop but I'm looking for solution using LINQ Only
Here is my working:
list.Select(x => x.Split('_').Select(s => new Item()
{
a1 = x[0],
a2 = x[1],
a3 = x[2]
}))
What you want to do is take each part of the split and put in a different property:
var result = list.Select(x => {
var parts = x.Split('_');
return new Item { a1 = parts[0], a2 = parts[1], a3 = parts[2] };
}).ToList();
Note that this does not check for value index passed to indexer. If you want to verify then you could:
var result = list.Select(x => {
var parts = x.Split('_');
return parts.Length == 3 ? new Item { a1 = parts[0], a2 = parts[1], a3 = parts[2] : null };
}).Where(i => i != null).ToList();
You need to split on '_' and your selects are off, you first need to select the Split result and then you index into the result array, your code indexes into the original string (x)
var list = new List<string>();
list
.Select(x => x.Split('_'))
.Select(s => new Item()
{
a1 = s[0],
a2 = s[1],
a3 = s[2]
});
How can I add a unique number to each of the distinct keys?
In the end I want a "collection" of the distinct keys but each key should have also a value which is e.g. the current_index_of_collection + 1
elements.SelectMany(p => p.Properties.Keys).Distinct();
sample output:
Key value
a 1
b 2
c 3
d 4
Are you looking for Select((value, index) => ...)?
https://msdn.microsoft.com/en-us/library/bb534869(v=vs.110).aspx
var dictionary = elements
.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((key, index) => new {
key = key,
value = index + 1,
})
.ToDictionary(item => item.key, item => item.value);
Or
var array = elements
.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((key, index) => new KeyValuePair<MyKeyType, int>(key, index + 1))
.ToArray();
You can use the Select overload which has an index field:
string[][] elements = new string[][] { new string[] { "a", "b", "a" } };
var elementsWithIndex = elements.SelectMany(p => p)
.Distinct()
.Select((p, i) => new { Key = p, Value = i + 1 });
Or in your code:
var elementsWithIndex = elements.SelectMany(p => p.Properties.Keys)
.Distinct()
.Select((p, i) => new { Key = p, Value = i + 1 });
You can simply use this.
List<string> keys = new List<string>();
keys.Add("a");
keys.Add("b");
keys.Add("c");
keys.Add("d");
keys.Add("e");
keys.Add("f");
keys.Add("g");
var fields = keys.Distinct().Select ((t,val)=> new { Key= t, Value= (val + 1)});
i've three Dictonaries like
Dictionary<int,List<string>> D1 = new Dictionary<int,List<string>>();
Dictionary<int,List<string>> D2= new Dictionary<int,List<string>>();
Dictionary<int,List<string>> D3 new Dictionary<int,List<string>>();
D1[1] = new List<string>{"a","b"};
D1[2] = new List<string>{"c","d"};
D1[3] = new List<string>{"e","f"};
D1[4] = new List<string>{"h"};
D2[1] = new List<string>{"a","b"};
D2[2] = new List<string>{"c","d"};
D2[3] = new List<string>{"e","f"};
D2[4] = new List<string>{"g"};
D2[5] = new List<string>{"b","h"};
D2[6] = new List<string>{"f","l"};
D2[7] = new List<string>{"z"};
i need to merge the two dictonary into a single dictonary
like
D3[1] = {"a","b","h"}
D3[2] = {"c","d"}
D3[3] = {"e","f","l"}
Merging Rule:
D1[1]={"a","b"} this list will be compared with the values in the D2
D2[1]={"a","b"}
D2[5]={"b","h"}
so the above three will be merged into
D3[1]={"a","b","h"}
is there any idea to do this using LINQ
However are you trying to merge the values, you will probably want to use one of these options:
D3[1] = D1[1].Union(D2[1]);
or
D3[1] = D1[1].Concat(D2[1]);
Edit - an ugly-looking method for joined merges Linq-style:
foreach (var kvp in D1)
{
D3[kvp.Key] =
(from string letter in kvp.Value
select
(from IEnumerable<string> list in D2.Values
where list.Contains(letter)
select list)
// Union all the D2 lists containing a letter from D1.
.Aggregate((aggregated, next) => aggregated.Union(next)))
// Union all the D2 lists containing all the letter from D1.
.Aggregate((aggregated, next) => aggregated.Union(next))
// Convert the unioned letters to a List.
.ToList();
}
The code keeps the lists in D2, it would be pretty easy to modify the code to remove the matched lists from D2.
Something like this (maybe needs optimisation)?
var lr =
(from gr in
(from pair in D1.Union(D2).Union(D3)
group pair by pair.Key)
select new KeyValuePair<int, IEnumerable<List<string>>>(gr.Key, gr.Select(x => x.Value))
).ToDictionary(k => k.Key, v => v.Value.Aggregate((t, s) => (new List<string>(t.Union(s)))));
I am trying to create a dictionary from 2 lists where one list contains keys and one list contains values. I can do it using for loop but I am trying to find if there is a way of doing it using LINQ.
Sample code will be helpfull. Thanks!!!!
In .NET4 you could use the built-in Zip method to merge the two sequences, followed by a ToDictionary call:
var keys = new List<int> { 1, 2, 3 };
var values = new List<string> { "one", "two", "three" };
var dictionary = keys.Zip(values, (k, v) => new { Key = k, Value = v })
.ToDictionary(x => x.Key, x => x.Value);
List<string> keys = new List<string>();
List<string> values = new List<string>();
Dictionary<string, string> dict = keys.ToDictionary(x => x, x => values[keys.IndexOf(x)]);
This of course assumes that the length of each list is the same and that the keys are unique.
UPDATE: This answer is far more efficient and should be used for lists of non-trivial size.
You can include the index in a Select expression to make this efficient:
var a = new List<string>() { "A", "B", "C" };
var b = new List<string>() { "1", "2", "3" };
var c = a.Select((x, i) => new {key = x, value = b[i]}).ToDictionary(e => e.key, e => e.value );
foreach (var d in c)
Console.WriteLine(d.Key + " = " + d.Value);
Console.ReadKey();
var dic = keys.Zip(values, (k, v) => new { k, v })
.ToDictionary(x => x.k, x => x.v);
You can use this code and working perfectly.
C# Code:
var keys = new List<string> { "Kalu", "Kishan", "Gourav" };
var values = new List<string> { "Singh", "Paneri", "Jain" };
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < keys.Count; i++)
{
dictionary.Add(keys[i].ToString(), values[i].ToString());
}
foreach (var data in dictionary)
{
Console.WriteLine("{0} {1}", data.Key, data.Value);
}
Console.ReadLine();
Output Screen: