How to use Linq and the IN clause - linq

Here is my code
if (catid != 0)
posts = posts.Where(x => x.catid IN '1,8,2,109,23');
The in in this code shows as a syntax error. Is there a way to fix this

You must use another list to compare too.
List<int> cadIdFoundList = new List<int>();
cadIdFoundList.Add(1);
cadIdFoundList.Add(8);
// etc. . .
posts.Where(x => cadIdFoundList.Contains(x.catId));

int[] ids = new int[] { 1, 8, 2, 109, 23 };
var query = posts.Where(x => ids.Contains(x.catid));
Rob Conery has discussed this topic before.

Or even more simple:
var query = posts.Where(x => new[] { 1, 8, 2, 109, 23 }.Contains(x.catid));

Maybe something more like:
HashSet<int> categories = new HashSet<int>() { 1, 2, 8, 23, 109};
posts = posts.Where(post => categories.Contains(post.catid));

Related

Group by with maximum

I want to group by category, show it's name, then show the highest id that is related to it. Here's some data and the result that I want further down. Any ideas? I've been playing around with GroupJoin but can't seem to get it to work.
My Data
var stuff = new[] {
new {id = 5, catId = 2},
new {id = 56, catId = 2},
new {id = 56, catId = 2},
new {id = 8, catId = 1},
new {id = 9, catId = 3}};
var categories = new[] {
new {catId = 1, Name = "Water"},
new {catId = 4, Name = "Wind"},
new {catId = 2, Name = "Fire"}};
What I want my results to look like
Water - 8
Wind - null
Fire - 56
categories
.GroupJoin
(
stuff,
c=>c.catId,
s=>s.catId,
(c,s)=>new
{
c.Name,
Max = s.Any() ? (int?)s.Max (m => m.id) : null
}
);
It seems that you want a "LEFT OUTER JOIN" with LINQ:
var query = from cat in categories
join s in stuff
on cat.catId equals s.catId into gj
from stuffJoin in gj.DefaultIfEmpty()
group stuffJoin by new { cat.catId, cat.Name } into catGroup
select new {
Category = catGroup.Key.Name,
MaxID = catGroup.Max(s => s == null ? 0 : s.id) // stuff is null for Wind
};
foreach (var x in query)
Console.WriteLine("Category: {0} Max-ID: {1}", x.Category, x.MaxID);
Outputs:
Category: Water Max-ID: 8
Category: Wind Max-ID: 0
Category: Fire Max-ID: 56

Order list by List<int> property

I need to order my list by points, then by positions. How can I order my list by the Positions List property?
public class Sailor
{
public string Name { get; set; }
public int Points { get; set; }
public List<int> Positions { get; set; }
public Sailor(string name, int points, List<int> positions)
{
Name = name;
Points = points;
Positions = positions;
}
}
var sailors = new List<Sailor>
{
new Sailor("Carl", 20, new List<int> { 2, 2, 4, 1, 1 }),
new Sailor("Paul", 10, new List<int> { 4, 5, 3, 2, 5 }),
new Sailor("Anna", 20, new List<int> { 1, 1, 1, 3, 4 }),
new Sailor("Lisa", 11, new List<int> { 3, 4, 5, 5, 2 }),
new Sailor("Otto", 11, new List<int> { 5, 3, 2, 4, 3 })
};
foreach (var sailor in sailors)
{
sailor.Positions.Sort();
}
var orderedListOfSailors = sailors.OrderByDescending(x => x.Points);
This gives me:
Carl, Anna, Lisa, Otto, Paul
What I want it to be:
Anna, Carl, Otto, Lisa, Paul
Why? Because Anna have 3 first places, Carl have 2. Otto have 2, 3, 3, Lisa have 2, 3, 4.
The problem can be solved by using lexicographical odering on instances of Sailor; either you could implement a custom comparator for Sailor or use the ThenBy extension method.
After ordering them by Points, order them again by the number of places.
var orderedListOfSailors = sailors
.OrderByDescending(x => x.Points)
.ThenByDescending(x => x.Positions.Count(y => y == 1))
.ThenByDescending(x => x.Positions.Count(y => y == 2))
.ThenByDescending(x => x.Positions.Count(y => y == 3))
.ThenByDescending(x => x.Positions.Count(y => y == 4));

Dynamic Linq Any syntax

I am trying to figure out how to use Any operator in Linq.Dynamic, here is my test code:
var bar = new[] {
new {Bar = "A", Id = 1},
new {Bar = "B", Id = 1},
new {Bar = "C", Id = 2},
new {Bar = "A", Id = 2},
new {Bar = "B", Id = 3},
new {Bar = "C", Id = 3}
};
var foo = new[] {
new {Foo = 1, Id = 1},
new {Foo = 1, Id = 1},
new {Foo = 2, Id = 2},
new {Foo = 2, Id = 2},
new {Foo = 2, Id = 3},
new {Foo = 3, Id = 3}
};
var res = foo.GroupJoin(bar, x => x.Id, y => y.Id, (x, l) => new { F = x.Foo, Bs = l.Select(b => b.Bar).ToArray() }).ToArray();
var normalLinq = res.Where(x => x.Bs.Any(y => y == "A")).ToArray();
var dynamicLinq = res.AsQueryable().Where("Bs.Any(x => x==\"A\")").ToArray(); //won't work
The syntax "Bs.Any(x => x==\"A\")" is wrong as dynamic linq doesn't seem to know the lambda expression I put in, and exception is thrown. How do I make it work?
My goal is to parse dynamic search input with this code. If you Linq experts have other better ways to do dynamic search, please suggest?
If you are using this library: http://www.nuget.org/packages/System.Linq.Dynamic/ as far as I know it does not allow lambda expressions. However, there is somebody who extended this library. I have never tried it but it looks promising. http://dynamiclinq.codeplex.com/ Good luck :)

using LINQ getting previous and next element

One of my colleague was looking for something like picking up previous and next values from a list for a given value. I wrote a little function with some help of Google, which works but I wanted to see
1. if is this an efficient way to do this?
2. Any other way in LINQ to do this?
private static List<double> GetHighLow(double value)
{
List<double> tenorList = new List<double> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 30 };
double previous = tenorList.OrderByDescending(s => s).Where(s => s.CompareTo(value) < 0).FirstOrDefault();
double next = tenorList.OrderBy(s => s).Where(s => s.CompareTo(value) > 0).FirstOrDefault();
List<double> values = new List<double> { previous, next };
return values;
}
thanks
Pak
Ordering just to find a single item would make me suspicious.
You can do it in linear time this way:
double prev = double.MinValue;
double nx = double.MaxValue;
foreach (var item in tenorList) {
if (item < value && item > prev) { prev = item; }
if (item > value && item < nx) { nx = item; }
}
List<double> values = new List<double> { prev, nx };

Simple LINQ query

I have a List of X items. I want to have LINQ query that will convert it into batches (a List of Lists), where each batch has 4 items, except for the last one which can have 1-4 (whatever the remainder is). Also, the number 4 should be configurable so it could 5, 17, etc.
Can anyone tell me how to write that?
List<Item> myItems = ...;
List<List<Item>> myBatches = myItems.????
Thank you in advance!
If you're happy with the results being typed as IEnumerable<IEnumerable<T>> then you can do this:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val);
If you want an actual List<List<T>> then you'll need to add a couple of extra ToList calls:
int groupSize = 4;
var myBatches = myItems.Select((x, i) => new { Val = x, Idx = i })
.GroupBy(x => x.Idx / groupSize,
x => x.Val,
(k, g) => g.ToList())
.ToList();
Here is a good article about using Take and Skip to do paging, which is identical functionality to what you are requesting. It doesn't get you all of the way to a single line of LINQ, but hopefully helps.
This made me think of how we did this before LINQ.
var vessels = new List<Vessel>()
{ new Vessel() { id = 8, name = "Millennium Falcon" },
new Vessel() { id = 4, name = "Ebon Hawk" },
new Vessel() { id = 34, name = "Virago"},
new Vessel() { id = 12, name = "Naboo royal starship"},
new Vessel() { id = 17, name = "Radiant VII"},
new Vessel() { id = 7, name = "Lambda-class shuttle"},
new Vessel() { id = 23, name = "Rogue Shadow"}};
var chunksize=2;
// With LINQ
var vesselGroups = vessels.Select((v, i) => new { Vessel = v, Index = i })
.GroupBy(c => c.Index / chunksize, c => c.Vessel, (t,e)=>e.ToList())
.ToList();
// Before LINQ (most probably not optimal)
var groupedVessels = new List<List<Vessel>>();
var g = new List<Vessel>();
var chunk = chunksize;
foreach(var vessel in vessels)
{
g.Add(vessel);
chunk--;
if (chunk == 0)
{
groupedVessels.Add(g);
g = new List<Vessel>();
chunk = chunksize;
}
}
groupedVessels.Add(g);

Resources