Is there a better way to write a LINQ function? - c#

So, I am new to LINQ and trying to figure out how to filter items.
That’s my task
public async Task<PagedList<Item>> GetItems (ItemParams itemParams) {
var items = _context.Items.AsQueryable ();
if (itemParams.CategoryId > 0) {
var category = GetCategory (itemParams.CategoryId);
items = items.Where (i => FilterItems (i, category.Result));
}
return await PagedList<Item>.CreatAsync (items, itemParams.PageNumber, itemParams.PageSize);
}
and the function that decides which items to return is
static bool FilterItems (Item item, Category category) {
if (item.CategoryId == category.Id) {
return true;
}
if (category.Children.Count > 0) {
foreach (Category cat in category.Children) {
return FilterItems (item, cat);
}
}
return false;
}
the get category func
public async Task<Category> GetCategory (int? id) {
if (id == null) {
return null;
}
var categories = _context.Categories.Include (x => x.Children).AsEnumerable ().Where (c => c.Id == id);
categories = Traverse (categories);
var category = await Task.FromResult (categories.First (c => c.Id == id));
return category;
}

Your filter will not work as expected, as the foreach returns at the first loop. Also the name FilterItems is not intuitive.
static bool ContainsItem(Category category, Item item)
{
return
category.Id == item.CategoryId ||
category.Children.Any(c => ContainsItem(c, item);
}
Since C# performs a short-circuit evaluation of the || Operator, the second term will not be evaluated if the first matches. Note that this is not a dirty trick, but is part of the C# specification.

From the code, it seems FilterItems() returns true if item is in current category or any sub-categories?
If that's the case, I think your current code has a problem: the recursive call to FilterItems() returns result immediately for the first sub-category without checking other sub-categories
Here is the modified code:
public static bool InCategoryOrSubCategory(Item item, Category category)
{
return item.CategoryId == category.Id ||
category.Children.Any(subCategory => InCategoryOrSubCategory(item, subCategory));
}
I change the name to InCategoryOrSubCategory to make it clearer
Here's a curried version, to make the Where call slightly nicer (but the method itself is a bit more cryptic):
public static Func<Item, bool> InCategoryOrSubCategory(Category category)
{
return item =>
item.CategoryId == category.Id ||
category.Children.Any(subCategory => InCategoryOrSubCategory(subCategory)(item));
}
Usage:
items.Where(InCategoryOrSubCategory(category))

Related

Are these two C# statements identical?

Items is a List<Item> of Items, one of the properties of which is ID.
public static Item ItemByID(int id)
{
foreach (Item item in Items)
{
if (item.ID == id)
{
return item;
}
}
return null;
}
And:
public static Item ItemByID(int id)
{
return Items.FirstOrDefault(item => item.ID == id);
}
I think it is but I am not sure FirstOrDefault() will return null if it doesn't find it.
Yes, the two blocks of code function the same way.

Find item if it exists

I have list with items and I check if an item exists in a List. If it exists, I try to find it.
I think that it has a little overhead, because I currently make two passes over the list. Is it possible to do in single pass?
Currently I have.
public partial class Item
{
public string text;
public int id;
}
....
static List<Item> data = new List<Item>();
static stub = new Item() { text = "NaN", id = -1 };
public static Item Get(int targetId)
{
if (data.Any(f => f.id == targetId) == false)
{
return stub;
}
return data.Find(f => f.id == targetId);
}
I want something like
...
public static Item Get(int targetId)
{
Item result;
result = data.Find(f => f.id == targetId);
if (result == null)
{
return stub;
}
return result;
}
You seem to be looking for FirstOrDefault():
Item _stub = new Item
{
text = "NaN",
id = -1
};
public Item FindByID(int id)
{
// Find the first item that has the provided id, or null if none exist.
var existingItem = data.FirstOrDefault(i => i.id == id);
// When not found, return the _stub
return existingItem ?? _stub;
}
You also may want to reconsider your naming conventions and whether you actually need these members to be static.
You can use List.FindIndex:
public static Item get(int i)
{
int index = data.FindIndex(item => item.id == i);
if (index == -1) return stub;
return data[index];
}
If it's actually an array you can use Array.FindIndex:
public static Item get(int i)
{
int index = Array.FindIndex(data, item => item.id == i);
if (index == -1) return stub;
return data[index];
}
So FirstOrDefault() is the way to go. You can also use SingleOrDefault() if there is only supposed to be one item in that list.
static stub = new Item() { text = "NaN", id = -1 };
public static Item get(int i)
{
Item result;
result = data.FirstOrDefault(f => f.id == i);
if (result == null)
{
return stub;
}
return result;
}
I use an extension method for exactly this purpose
public static T FirstOrSpecificDefault<T>(this IEnumerable<T> list,
Func<T, bool> predicate, T defaultValue)
{
return list.FirstOrDefault(predicate) ?? defaultValue;
}
usage in your case would be
Item result = list.FirstOrSpecificDefault(f => f.id == i, stub);
I think you can try this:
return data.Find(f => f.id == i) ?? stub;

LINQ query with boolean check

I need a method which should return a list of stores from stores table, where customerid = id and Isactive = "true".
I am able to get the customerid match like so, how can I also include the Boolean check... need help with the query syntax..the "AND" operator
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id).ToList();
return (stlist);
}
Assuming that Isactive is a property of records in db.Stores, like CustomerId is
You can just add the additional check inside the Where extension method:
Assuming that Isactive is a property of type bool
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive).ToList();
return (stlist);
}
But if Isactive is a property of type string as OP seems to indicate
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive == "true").ToList();
return (stlist);
}
In C# and many other languages, && is the Boolean AND operator.
private IList<Store> GetStores(int id)
{
var stlist = db.Stores.Where(m=>m.CustomerId == id && m.IsActive == true).ToList();
return stlist;
}
var stlist = db.Stores.Where(m => m.CustomerId == id && m.Isactive == "true").ToList();

How do you create a dynamic select projection from a generic using Linq in C#?

So I have a function that I pass Func call back to. I would also like to add some sort of selection projection to be able to do the projection on the object, meaning I would only perform one database call. The function looks something like this:
public T Get<T>(string id, Func<T> getItemCallback) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomeThing(item);
// Here I would like to call something else that is
// expecting a specific type. Is there way to pass in a
// dynamic selector?
doSomethingElse(item.Select(x => new CustomType { id = x.id, name = x.name }).ToList());
}
return item;
}
void doSomethingElse(List<CustomType> custom)
{
....
}
Leme show how I cam currently calling this perhaps that will help:
public List<MyDataSet> GetData(string keywords, string id)
{
return _myObject.Get(
id,
() => db.GetDataSet(keywords, id).ToList());
// Perhaps I could add another parameter here to
// handled the projection ????
}
Thanks to Reed I figured it out...would look like this:
public T Get<T>(string id, Func<T> getItemCallback, Func<T, List<CustomType>> selector) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomething(item);
var custom = selector(item);
if (custom != null)
{
doSomethingElse(custom);
}
}
return item;
}
And The call looks like:
public List<MyDataSet> GetData(string keywords, string id)
{
return _myObject.Get(
id,
() => db.GetDataSet(keywords, id).ToList(),
x => x.Select(d => new CustomType { id = d.ReferenceId, name = d.Name })
.ToList());
}
You would need to also pass in a conversion function:
public T Get<T>(string id, Func<T> getItemCallback, Func<T, List<CustomType>> conversion) where T : class
{
item = getItemCallback();
if (item != null)
{
doSomeThing(item);
if (conversion != null)
doSomethingElse(conversion(item));
}
return item;
}

If else alternative for a conditional operator filter in a predicate?

After a long search i have my first question.
I have this piece of code:
var strings = new MyStringList { "orange", "APPLE", "grape", "pear" };
foreach (var item in strings.Where(s => s.Length == 5))
{
txtLog.WriteLine(item);
}
And a public class:
public class MyStringList : List<string>
{
public IEnumerable<string> Where(Predicate<string> filter)
{
return this.Select(s => filter(s) ? s.ToUpper() : s);
}
}
Is there a way to rewrite the return statement in an If Else construction?
I came to something like this, only the if gives an error:
if (this.Select(s=> filter(s)))
{
return this.Select(s => s.ToUpper());
}
else
{
return this.Select(s => s);
}
Since your filter works on a single item, you have to iterate through your collection first. During iteration there is no longer any need to use Select() instead you use yield return to dynamically return an iterator.
public IEnumerable<string> Where(Predicate<string> filter)
{
foreach (var s in this)
{
if (filter(s))
yield return s.ToUpper();
else
yield return s;
}
}

Categories