I have the following foreach statements, which I want to transfer into linq query.
var equalityGroup= new Dictionary<string, List<string>();
var firstGroup = new Dictionary<string, List<string>();
var request = new List<Request>();
foreach(var element in request)
{
var key = element.Number;
if (!equalityGroup.ContainsKey(key))
{
equalityGroup.Add(key, new List<string>());
}
foreach(var item in firstGroup)
{
var query = item.Value.FindAll(y => y ==element.Id);
if (query.Any())
{
equalityGroup[key].AddRange(query);
}
}
}
Can someone give me a good example for Linq that will work as this foreaches?
I think you should be able to greatly increase the performance for large collections by performing a join (which uses hash-tables internally) instead of nested loops.
var firstGroup = new Dictionary<string, List<string>>();
var request = new List<Request>();
var q = from element in request
join y in firstGroup.SelectMany(x => x.Value) on element.Id equals y
group y by element.Id into g
select new { g.Key, g };
var equalityGroup = q.ToDictionary(x => x.Key, x => x.g.ToList());
I have two generic lists. They have mostly different fields, but there are 4 fields they have in common. I want to get the list of items that are in one of the lists but not the other using those four fields as the definition of "equality".
Here was my attempt at solving the problem.
var unMatchedData = from liveLines in liveList
join oldList in comapreSnapshotList
on new {liveLines.ClientNo, liveLines.SequenceNo, liveLines.LineNo, liveLines.Text} equals
new {oldList.ClientNo, oldList.SequenceNo, oldList.LineNo, oldList.Text}
select new KNOWTXTS
{
ClientNo = liveLines.ClientNo,
SequenceNo = liveLines.SequenceNo,
LineNo = liveLines.LineNo,
Text = liveLines.Text
};
You can use Except to find the set difference.
var newElements = liveList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var oldElements = comapreSnapshotList.Select(l => new {
l.ClientNo, l.SequenceNo, l.LineNo, l.Text
});
var newElementsInNew = newElements.Except(oldElements);
var deletedFromNew = oldElements.Except(newElements);
// if you need the original object in the list
var newElements = from obj in liveList
join newEle in newElementsInNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals newEle
select obj;
var deletedElements = from obj in comapreSnapshotList
join deletedEle in deletedFromNew
on new {obj.ClientNo, obj.SequenceNo, obj.LineNo, obj.Text} equals deletedEle
select obj;
var a = new []{"a"};
var b = new []{"b"};
var c = new []{"c"};
Is it possible to declare / initialise a generic list, providing the three collections above in one line?
var l = new List<string>(a); //fine for one
var l2 = new List<string>(new[] { a, b, c }.SelectMany(x => x)); //this will work but its horrible!
How about:
var list = a.Concat(b).Concat(c).ToList();
i have two sorted dictionaries both with the type signature
i.e.
SortedDictionary<decimal, long> A
SortedDictionary<decimal, long> B
I want to merge the two lists where the key is the same, thus creating a new list like
SortedDictionary<decimal, KeyValuePair<long,long>>
or
SortedDictionary<decimal, List<long>>
This may not be the best way of approacing the situation but could someone give me a heads up on how to do this or a better way to approach it.
This is what I've got:
SortedDictionary<decimal, List<long>> merged = new SortedDictionary<decimal, List<long>>
(
A.Union(B)
.ToLookup(x => x.Key, x => x.Value)
.ToDictionary(x => x.Key, x => new List<long>(x))
);
EDIT: Above solution selects keys not included in both collections. This should select where keys are same:
SortedDictionary<decimal, List<long>> merged = new SortedDictionary<decimal, List<long>>
(
A.Where(x=>B.ContainsKey(x.Key))
.ToDictionary(x => x.Key, x => new List<long>(){x.Value, B[x.Key]})
);
You can do this simply using LINQ:
var query = from a in A
join b in B
on a.Key equals b.Key
select new {
Key = a.Key,
Value = Tuple.Create(a.Value, b.Value)
};
var merged = new SortedDictionary<decimal, Tuple<long, long>>(
query.ToDictionary(x => x.Key, x => x.Value)
);
I think you should use Tuple<long, long> as your TValue in the merged dictionary.
Another LINQ way of doing this that I think captures the intent better in terms of set operations:
SortedDictionary<decimal, long> a = new SortedDictionary<decimal, long>();
SortedDictionary<decimal, long> b = new SortedDictionary<decimal, long>();
a.Add(0, 10);
a.Add(1, 10);
a.Add(2, 100);
a.Add(100, 1);
b.Add(0, 4);
b.Add(4, 4);
b.Add(2, 10);
var result = a.Union(b)
.GroupBy(x => x.Key)
.ToDictionary(x => x.Key, x => x.Select(y => (long)y.Value).ToList());
Try something like this, it not easy:
Dictionary<decimal, long> dic1 = new Dictionary<decimal, long>{ {3,23}, {2,3}, {5,4}, {6,8}};
Dictionary<decimal, long> dic2 = new Dictionary<decimal, long>{ {3,2}, {2,5}, {5,14}, {12,2}};
//recover shared keys (the keys that are present in both dictionaries)
var sharedKeys = dic1.Select(dic => dic.Key).Intersect(dic2.Select(d2=>d2.Key));
sharedKeys.Dump();
//add to the fìnal dictionary
var final = new Dictionary<decimal, List<long>>();
foreach(var shk in sharedKeys) {
if(!final.ContainsKey(shk))
final[shk] = new List<long>();
final[shk].Add(dic1[shk]);
final[shk].Add(dic2[shk]);
}
**EDIT**
//Skip below part if you need only keys present on both dictionaries.
///-----------------------------------------------------------------
//get unique keys present in Dic1 and add
var nonsharedkeys1 = dic1.Select(d=>d.Key).Where(k=>!sharedKeys.Contains(k));
foreach(var nshk in nonsharedkeys1) {
final[nshk] = new List<long>();
final[nshk].Add(dic1[nshk]);
}
//get unique keys present in Dic2 and add
var nonsharedkeys2 = dic2.Select(d=>d.Key).Where(k=>!sharedKeys.Contains(k));
foreach(var nshk in nonsharedkeys2) {
final[nshk] = new List<long>();
final[nshk].Add(dic2[nshk]);
}
Should work for you.
You could "abuse" Concat and Aggregate like this:
var A = new SortedDictionary<decimal,long>();
var B = new SortedDictionary<decimal,long>();
A.Add(1, 11);
A.Add(2, 22);
A.Add(3, 33);
B.Add(2, 222);
B.Add(3, 333);
B.Add(4, 444);
var C = A.Concat(B).Aggregate(
new SortedDictionary<decimal, List<long>>(),
(result, pair) => {
List<long> val;
if (result.TryGetValue(pair.Key, out val))
val.Add(pair.Value);
else
result.Add(pair.Key, new[] { pair.Value }.ToList());
return result;
}
);
foreach (var x in C)
Console.WriteLine(
string.Format(
"{0}:\t{1}",
x.Key,
string.Join(", ", x.Value)
)
);
The resulting output:
1: 11
2: 22, 222
3: 33, 333
4: 444
This is pretty much the same as if you wrote a "normal" foreach and would in fact work on any IEnumerable<KeyValuePair<decimal, long>> (not just SortedDictionary<decimal, long>) and is easy to extend to more than two input collections if needed.
Unfortunately, it also completely disregards the fact that the input SortedDictionary is, well, sorted, so performance is not optimal. For optimal performance you'd have to fiddle with linearly advancing separate IEnumerator for each of the input sorted dictionaries, while constantly comparing the underlying elements - you could completely avoid TryGetValue that way...
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.