Simplify conventional foreach nested loop using linq & lambda expression - c#

(See my code snippet below) I want to find all items of coll1 that matched to items of coll2 (number of items of coll2 <= number of items of coll1) and put the query result in coll3. How to achieve it using linq and lambda expression?
Surely, I can simply copy coll2 to coll3 :-) but that is not my goal. I want to know the ways using linq and lambda to replace such conventional logic construct. Thank you in advance.
var coll1 = new List<int>() { 1, 2, 3, 4, 5 };
var coll2 = new List<int>() { 2, 4 };
var coll3 = new List<int>();
foreach ( var selected in coll2 )
{
foreach ( var item in coll1 )
{
if ( selected == item )
{
coll3.Add(item);
}
}
}

You can use Intersect
coll1.Intersect(coll2);
But this wont work as expected(see King King's comment)..You can instead do this
coll2.Where(x=>coll1.Any(y=>x==y));

coll3 = coll1.Where(i => coll2.Contains(i)).ToList();
Update. A little bit simpler, as suggested in comments:
coll3 = coll1.Where(coll2.Contains).ToList();

As a first step, you can use a where clause to make your code more readable :
var coll1 = new List<int>() { 1, 2, 3, 4, 5 };
var coll2 = new List<int>() { 2, 4 };
var coll3 = new List<int>();
foreach (var selected in coll2)
{
coll3.AddRange(coll1.Where(item => selected == item));
}

Use Intersect: http://msdn.microsoft.com/en-us/library/bb460136.aspx
var coll3 = coll1.Intersect(coll2)

You could do this; not sure if it's more readable tho!
var coll3 = (from selected in coll2
from item in coll1
where selected == item
select item).ToList();

If you do not want to directly assign the result to your list, you could add 'ForEach' to your Linq statement:
coll1.Where(i => coll2.Contains(i)).ToList().ForEach(i => coll3.Add(i));
You might want to check for Distinct though

Related

Linq GroupBy converted to a conditional?

If I have the following List
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
and I want to check for duplicates, so I use GroupBy:
Values.GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key)
How do I make this work in a While conditional/convert to boolean so that, if and only if there are any duplicates present in Values, evaluate to true, otherwise, evaluate false using Linq? I suspect I need to use .Any somehow, but I'm not sure where to fit it in.
Simply check before adding randomly generated number in the list
do
{
var randomNumber = //Generate number here
if(!Values.Contains(randomNumber))
{
Values.Add(randomNumber);
break;
}
}while(true);
The root problem here, is that you are using a Select call. The Select call you are using will return an IEnumerable of longs, not a boolean.
The only way code like that would compile as part of a boolean (be it as a bool variable or as a check done inside an If, While, etc), is if you remove both the Select and the Where calls, and replace it with an Any:
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
Values.GroupBy(x => x).Any(g => g.Count() > 1);
If you then need to find out what the duplicates actually are, then the best way to do this would be to store the results of the Group call in a variable, and then use that variable to call Any in your boolean check, and grab the items from the variable when you want to report on them:
List<int> Values = new List<int>() { 1, 5, 6, 2, 5 };
var duplicateValues = Values.GroupBy(x => x).Where(g => g.Count() > 1);
bool anyDuplicates = duplicateValues.Any();
var duplicateKeys = duplicateValues.Select(x => x.Key).ToList();
You can use combination of foreach loop and return value of HashSet<int>.Add method.
With this approach you don't need to loop all values if duplication have been found early. Where GroupBy should always loop all values.
public static bool HaveDuplicates(this IEnumerable<int> values)
{
var set = new HashSet<int>();
foreach (var value in values)
{
if (set.Add(value) == false)
{
return true;
}
}
return false;
}
Use it as extension method
var values = new List<int>() { 1, 5, 6, 1, 2, 5, 6, 7, 8, 9 };
if (values.HaveDuplicates())
{
// have duplicates
}
I'm trying to use it in a do while loop. Inside the do, I generate a
random number from 1 to 9, and add it to the Values list. if the
number that gets generated is one already in the list, then run the
loop over again. If not, escape the loop
Use HashSet<T> for saving generated number. HashSet<T> will check for duplication with O(1) operations, where
var values = new HashSet<int>();
do
{
var generatedValue = GenerateNumber(); // your generation logic
if(values.Add(generatedValue) == false)
{
break;
}
}
while(yourCondition);
You can loop HashSet as other collections or convert it to List if you need
var numbers = values.ToList();

MySql ORDER BY FIELD() in EntityFramework

I have a list of numbers and need to select rows from DB table by that order.
i was looking to implement ORDER BY FIELD() query in LinQ / lambda expressions with no luck
any ideas?
the code look like this:
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var query = db.place_users_info.OrderBy(item => item.UniqueId);
}
I need to order the rows by the list items
if your rowcount is not that big, maybe you are looking for something like this (checks ommitted):
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var a = db.place_users_info.ToArray();
var b = list.Select(x=>a[x]).ToList();
}
From what I understand you have (according to the example) 4 rows which you want to order by the number they have on the number list.
To do so use Zip to merge the lists by index (first item in numbers list will be with first item in the data list and so on)
using (var db = new TimeTable.EntityFramework.TimeTableEntities())
{
List<int> list = new List<int>() { 2, 1, 4, 3 };
var query = db.place_users_info.Zip(list, (f,s) => new { f,s })
.OrderBy(item => item.s)
.Select(item => item.f);
}
If you have the list, say:
var list<myobject> = <yourRepository>.<GetYourData>.ToList().OrderBy(o => o.<property>;
besides, in memory order by is probably faster then in DB.
If you would do:
var list<myobject> = <yourRepository>.<GetYourData>.OrderBy(o => o.<property>).ToList();
you would do the order by in the DB. (If your returns an IQueryable)

Using C# lambdas to combine List<int> and int

I have the following list of Pair objects:
var listOfPairs = new List<Pair<int, List<int>>>() {
new Pair<int, List<int>>(30, new List<int>() {3, 6, 9}),
new Pair<int, List<int>>(40, new List<int>() {4, 8, 12})
};
I would like to end up with the following list of list-of-integers:
listOfPairs[0] = {30, 3, 6, 9};
listOfPairs[1] = {40, 4, 8, 12};
I've tried a lot of fiddling that looks like this, but to no avail:
var flattenedListOfPairs = listOfPairs.Select(pair => new List<int>(pair.First).AddRange(pair.Second));
I assume that what I'm trying to do is possible, and I'm just missing something.
Sounds like you might want something like:
var flattened = listOfPairs.Select(pair => new[] { pair.First }.Concat(pair.Second)
.ToList())
.ToList();
Or:
var flattened = listOfPairs.Select(pair => Enumerable.Repeat(pair.First, 1)
.Concat(pair.Second)
.ToList())
.ToList();
Or using MoreLINQ
var flattened = listOfPairs.Select(pair => pair.Second.Prepend(pair.First)
.ToList())
.ToList();
This gives you a list of lists, in the form you specified:
listOfPairs.Select(p => new []{ p.First }.Concat(p.Second).ToList()).ToList()
Other answers already covered how to do this, so I won't repeat that here. This answer is to explain why your existing code wasn't working. You expected to pass an int to the List constructor and have it initialize the List with that int. That's not how the constructor works. The List constructor uses the int argument to set up the initial size of the list, rather than set the value of any items.
Try this:
var flattenedListOfPairs = listOfPairs.Select(pair =>
{
var list = new List<int>(pair.First);
list.AddRange(pair.Second));
return list;
}.ToList();

Intersection of 6 List<int> objects

As I mentioned in the title I've got 6 List objects in my hand. I want to find the intersection of them except the ones who has no item.
intersectionResultSet =
list1.
Intersect(list2).
Intersect(list3).
Intersect(list4).
Intersect(list5).
Intersect(list6).ToList();
When one of them has no item, normally I get empty set as a result. So I want to exclude the ones that has no item from intersection operation. What's the best way to do that?
Thanks in advance,
You could use something like this:
// Your handful of lists
IEnumerable<IEnumerable<int>> lists = new[]
{
new List<int> { 1, 2, 3 },
new List<int>(),
null,
new List<int> { 2, 3, 4 }
};
List<int> intersection = lists
.Where(c => c != null && c.Any())
.Aggregate(Enumerable.Intersect)
.ToList();
foreach (int value in intersection)
{
Console.WriteLine(value);
}
This has been tested and produces the following output:
2
3
With thanks to #Matajon for pointing out a cleaner (and more performant) use of Enumerable.Intersect in the Aggregate function.
Simply, using LINQ too.
var lists = new List<IEnumerable<int>>() { list1, list2, list3, list4, list5, list6 };
var result = lists
.Where(x => x.Any())
.Aggregate(Enumerable.Intersect)
.ToList();
You could use LINQ to get all the list that are longer then 0 , and then send them to the function you've described.
Another option :
Override/Extend "Intersect" to a function that does Intersect on a list only if it's not empty , and call it instead of Intersect.

Working on lambda expression

I am squaring each integer in a List. Here is the code.
class SomeIntgs
{
List<int> newList = new List<int>();
public List<int> get()
{
IEnumerable<int> intrs = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
newList.AddRange(intrs);
return newList;
}
}
I am getting error in Main()
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() where (P => P*P) select n;
Error : "Can not convert lambda expression to type bool ".
Help Please.
Also help me, how can i handle lambda in general context
You don't need the where, try this:
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() select n*n;
or
var qry = stg.get().Select(P => P*P);
Enumerable.Where is used to filter elements from a sequence - what you really want to do is project a new sequence of elements like I have shown above.
The lambda that the where clause takes specifies how you match an item from your IQueryable. Any member of the IQueryable that satisfies the expression you supply will be returned. (This is why your compiler is complaining about bools).
As others have mentioned, you can drop the where clause to square each item in the list.
var ints = new int []{1,2,3,4,5,6,7,8};
var squares = ints.Select(x => x*x);
var evenSquares = ints.Where(x => (x % 2) == 0).Select(x => x*x); // only square
//the even numbers in the list
SomeIntgs stg = new SomeIntgs();
var qry = from n in stg.get() select n*n;

Categories