LINQ for nested for each - linq

Can you please give me a solution for the method as Linq?
I need Linq for the below method:
private List<Model> ConvertMethod(List<Model> List1, List<Model> List2)
{
foreach (var Firstitem in List1)
{
foreach (var Seconditem in List2)
{
if (Firstitem.InnerText.Trim() == Seconditem.InnerText.Trim())
{
Seconditem.A= Firstitem.A;
Seconditem.B= Firstitem.B;
Seconditem.C= Firstitem.C;
Seconditem.D= Firstitem.D;
Seconditem.E= Firstitem.E;
Seconditem.F= Firstitem.F;
}
}
}
return List2;
}

Your task is to assign values, so modify objects. That's not the purpose of LINQ which is to query datasources. So you could use LINQ to build a query that returns all items that need to be updated. Then you can use a foreach to assign the values(as you did):
var sameItems = from l1 in List1 join l2 in List2
on l1.InnerText.Trim() equals l2.InnerText.Trim()
select new { l1, l2 };
foreach(var itemsToUpdate in sameItems)
{
itemsToUpdate.l2.A = itemsToUpdate.l1.A;
// ...
}

It helps if you think about what this code is supposed to do - update records in the second list with the values of matching records from the first list.
There are various ways you can do that. One option is to replace each foreach with from and filter the rows:
var matches = from var Firstitem in List1
from var Seconditem in List2
where Firstitem.InnerText.Trim() == Seconditem.InnerText.Trim()
select (Firstitem,Seconditem);
foreach(var (Firstitem,Seconditem) in matches)
{
Seconditem.A= Firstitem.A;
Seconditem.B= Firstitem.B;
Seconditem.C= Firstitem.C;
Seconditem.D= Firstitem.D;
Seconditem.E= Firstitem.E;
Seconditem.F= Firstitem.F;
}
I'm "cheating" a bit here, using tuples and decomposition to reduce noise
Another option is to use join. In this case, the two options are identical :
var matches = from Firstitem in List1
join Seconditem in List2
on Firstitem.InnerText.Trim() equals Seconditem.InnerText.Trim()
select (Firstitem,Seconditem);
The rest of the code remains the same

Related

Linq Query Filter from two lists where row differs

Not sure how to formulate this Linq query.
I have two lists, each of which contains HashCheck objects:
class HashCheck
{
public string Id {get; set;}
public string Hash {get; set;}
}
So, given
List<HashCheck> list1;
List<HashCheck> list2;
I need a query that will result in a list having rows where the Ids of the rows matches, but the Hash does not.
So for example
List1 =
{1, 12345,
2, 34323,
3, 34083,
4, 09887}
List2 =
{1, 00001, << matching id, not matching hash
2, 34323,
3, 11112, << matching id, not matching hash
4, 09887
5, 98845}
ResultList =
{1, 00001,
3, 11112}
NOTE: in List2, there is an extra row, it would be a bonus if this were included in the ResultList. But I know how to do that in a separate query if necessary.
Thanks for any help.
try this code:
var list3 = (from i in list1
from j in list2
where i.Id == j.Id && i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
You can use join. something like below code:
var list3 = (from i in list1
join j in list2 on i.Id equals j.Id
where i.Hash != j.Hash
select new HashCheck() { Id = j.Id, Hash = j.Hash
}).ToList<HashCheck>();
It looks like you want your result to contain the HashCheck objects from list2, which would simply mean:
var ans = list2.Where(hc2 => !list1.Any(hc1 => hc1.Id == hc2.Id && hc1.Hash == hc2.Hash));
e.g. return all list2 elements without a list1 element that matches in both Id and Hash.
If list1 (and/or list2) is very large and performance is a consideration, you can convert list1 to a Dictionary and do lookups against that:
var list1map = list1.ToDictionary(hc1 => hc1.Id, hc1 => hc1.Hash);
var ans2 = list2.Where(hc2 => !list1map.TryGetValue(hc2.Id, out var hash1) || hash1 != hc2.Hash);
Another alternative would be to implement Equals/GetHashCode for your class and then you can use LINQ Except.
Add the following methods to your class:
public override bool Equals(object other) => (other is HashCheck hco) ? Id == hco.Id && Hash == hco.Hash : false;
public override int GetHashCode() => (Id, Hash).GetHashCode();
Now the computation is simple:
var ans3 = list2.Except(list1);
NOTE: Implementing Equals/GetHashCode in this way can be problematic if your HashCode objects are not treated as immutable. Some collection classes really won't like it if the hash code of an object already stored in them changes.
Also, it would be best practice to implement operator== and operator!= as well and possibly IEquatable.

Linq: Ignore non joinable items from two lists without throwing error?

In the code below, the two lists are joined on Index. But either list could have more items than the other and i just want to join up to the list with the least items and throw the rest out from the other list. So, if list 1 has 5 items and list 2 has 7 items, I want to join both up to item 5, and ignore list 2's remaining items. (and vice versa)
var joinLbxs = lbxShtCols.Items
.Cast<ListItem>()
.Select((xlFldList, index) => new
{
xlFldList,
tblFldList = lbxSqlTablesCols.Items[index]
});
Zip is not too complicated to implement by yourself.
public static IEnumerable<TResult> Zip<TSource, TOther, TResult>(
this IEnumerable<TSource> source,
IEnumerable<TOther> other,
Func<TSource, TOther, TResult> resultSelector)
{
using (var e1 = source.GetEnumerator())
{
using (var e2 = other.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext())
{
yield return resultSelector(e1.Current, e2.Current);
}
}
}
}
As #Steven suggested in a comment, if you are using .Net 4.0, use the Zip() method. If you don't, you could use MoreLinq to provide the same functionality.
Or you could do it yourself (assuming both lists are IList<T> and have fast indexer):
from i in Enumerable.Range(0, new[] { list1.Count, list2.Count }.Min())
select new
{
item1 = list1[i],
item2 = list2[i]
}
Try intersecting the 2 lists; that'll give you the common items. Take() the number smallest of the 2 lists that you want. It's not clear whether you know which list would have the smallest (by convention), so decide that beforehand. Optionally sort the list if you need BEFORE the Take().
int numToTake = (lbxShtCols.Count >= lbxSqlTablesCols.Count)
?lbxShtCols.Count
:lbxSqlTablesCols.Count;
var commons = lbxShtCols.Intersect(lbxSqlTablesCols)
.Take(numToTake);

Calling a function within a Linq query

If I want to iterate through a collection, and call a function on each element in the collection, I could go with :
foreach (var obj in objColl)
{
MyFunction(obj);
}
Should I want to do this with linq, I can use either of those :
//#1
var unused = (from var obj in objColl select MyFunction(obj)).ToList();
//#2
var unused = objColl.Select(obj => MyFunction(obj)).ToList();
I know this works, but it doesn't seem right. Of course, my actual cases are more complex queries that that, but it comes down to this since I could build my IQueryable with Linq and iterate through it and call the function.
Edit:
Here is one example of what I did. (Item# are things I can't disclose)
var dummyValue = (from
Item7 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item1" && age.TargetName == "Item2")
join Item6 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item2" && age.TargetName == "Item3") on Item6.TargetValue equals Item7.SourceValue
join agency in dal.GetAgencies() on Item7.SourceValue equals agency.Agency
orderby Item7.TargetValue
select vl.ValueListItems.Add(agency.ID, Item7.TargetValue)).ToList();
Go with the simple foreach, as you are clearly wanting to perform an action on (and/or using) the objects in your collection as opposed to wishing to project/filter/group/etc. the items in the sequence. LINQ is about the latter set of operations.
Edit: In the case of your update, I would simply create a query, and then iterate over the query in the foreach to perform the action.
var query = from Item7 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item1" && age.TargetName == "Item2")
join Item6 in dal.GetAgencyConvertions().Where(age => age.SourceName == "Item2" && age.TargetName == "Item3") on Item6.TargetValue equals Item7.SourceValue
join agency in dal.GetAgencies() on Item7.SourceValue equals agency.Agency
orderby Item7.TargetValue
select new { ID = agency.ID, Value = Item7.TargetValue };
foreach (var item in query)
vl.ValueListItems.Add(item.ID, item.Value);
To be frank, you have the same loop happening in your code, you merely mask it by using the ToList() extension method. As a byproduct, you are creating a list of values that you have no intention of using, while somewhat obfuscating the true intention of the code, all to save maybe a few characters.
Typically, a query shouldn't have any side effects (i.e. it shouldn't modify the state of the data or other data in your application) which raises the question, does MyFunction modify the state of your application? If it does, then you should use a foreach loop.
How about an Each() extension method?
public static void Each<T>(this IEnumerable<T> target, Action<T> action)
{
if (target == null) return;
foreach (T obj in target)
action(obj);
}

How to get values after dictionary sorting by values with linq

I've a dictionary, which i sorted by value with linq, how can i get those sorted value from the sorted result i get
that's what i did so far
Dictionary<char, int> lettersAcurr = new Dictionary<char, int>();//sort by int value
var sortedDict = (from entry in lettersAcurr orderby entry.Value descending select entry);
during the debug i can see that sortedDic has a KeyValuePar, but i cant accesses to it
thanks for help
sortedDict is IEnumerable<KeyValuePair<char, int>> iterate it
Just iterate over it.
foreach (var kv in sortedDict)
{
var value = kv.Value;
...
}
If you just want the char values you could modify your query as:
var sortedDict = (from entry in lettersAcurr orderby entry.Value descending select entry.Key);
which will give you a result of IEnumerable<char>
If you want it in a dictionary again you might be tempted to
var q = (from entry in lettersAcurr orderby entry.Value descending select entry.Key).ToDictionary(x => x);
but do bare in mind that the dictionary will not be sorted, since the Dictionary(Of T) will not maintain the sorted order.

Join 2 lists by order instead of condition in LINQ

How can I join 2 lists of equal lengths (to produce a 3rd list of equal length) where I do not want to specify a condition but simply rely on the order of items in the 2 lists.
Eg how can I join:
{1,2,3,4} with {5,6,7,8}
to produce:
{{1,5}, {2,6}, {3,7}, {4,8}}
I have tried the following:
from i in new []{1,2,3,4}
from j in new []{5,6,7,8}
select new { i, j }
but this produces a cross join. When I use join, I always need to specify the "on".
You could use Select in the first list, use the item index and access the element on the second list:
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var qry = a.Select((i, index) => new {i, j = b[index]});
If you are using .Net 4.0, you can use the Zip extension method and Tuples.
var a = new [] {1,2,3,4};
var b = new [] {5,6,7,8};
var result = a.Zip(b, (an, bn) => Tuple.Create(an, bn));
Alternatively, you can keep them as arrays:
var resultArr = a.Zip(b, (an, bn) => new []{an, bn});
There is a half way solution, if you want to use query syntax. Half way in the sense that you need to use the Select method on both lists in order to get the indexes that you will use in the where clause.
int[] list1 = {1,2,3,4};
int[] list2 = {5,6,7,8};
var result = from item1 in list1.Select((value, index) => new {value, index})
from item2 in list2.Select((value, index) => new {value, index})
where item1.index == item2.index
select new {Value1 = item1.value, Value2 = item2.value};
The benefit with this solution is that it wont fail if the lists have different lengths, as the solution using the indexer would do.

Resources