i have class strucur something like this
List<MainCat> AllCat;
public class MainCat
{
public string id { get; set; }
public string name { get; set; }
public List<subcat> subcat { get; set; }
}
public class subcat
{
public string id { get; set; }
public string name { get; set; }
public List<subsubcat> subsubcat { get; set; }
}
public class subsubcat
{
public string id { get; set; }
public string name { get; set; }
}
i want to get name by id,
for example i know the id is 69
i want get output like this
MainCat.name > subcat.name > subsubcat.name (if 69 found in subsubcat)
MainCat.name > subcat.name (if 69 found in subcat)
MainCat.name (if 69 found in MainCat)
If I've understood your requirement properly, this is a case where the query syntax can work wonders:
IEnumerable<string> MyFunc(IEnumerable<MainCat> mainCategories, string idToMatch)
{
return (from main in mainCategories
where main.id == idToMatch
select main.name)
.Concat(from main in mainCategories
from sub in main.subcat
where sub.id == idToMatch
select string.Format("{0} > {1}", main.name, sub.name))
.Concat(from main in mainCategories
from sub in main.subcat
from subsub in sub.subsubcat
where subsub.id == idToMatch
select string.Format("{0} > {1} > {2}", main.name, sub.name, subsub.name));
}
If you're only interested in the first match, this can be called like
string resultName = MyFunc(AllCat, "69").FirstOrDefault();
Because the query uses deferred execution, this will avoid calling the more complex queries if a match is found in the main category.
It is also possible to use the SelectMany function with the function call syntax, however, it gets much harder to follow e.g. the following is how I re-wrote the contents of the second .Concat(...) call in order to illustrate:
mainCategories.SelectMany(main => main.subcat, (main, sub) => new { Main = main, Sub = sub })
.SelectMany(pair => pair.Sub.subsubcat, (pair, subsub) => new { Main = pair.Main, Sub = pair.Sub, SubSub = subsub})
.Where(triplet => triplet.SubSub.id == idToMatch)
.Select(triplet => string.Format("{0} > {1} > {2}", triplet.Main, triplet.Sub, triplet.SubSub));
As I understand it, the query syntax compiles to something very similar to this behind the scenes.
Update after answer accepted, and I came back to look at my code again:
Another possibility would be to add an interface to all 3 classes (or unify them into a single class or derive from a common base class depending on real use case).
This allows a recursive implementation that can search to arbitrary depth (below are 2 different Linq-based implementations depending on whether you have a preference for one or other syntax):
public interface ITreeCat
{
string id { get; }
string name { get; }
IEnumerable<ITreeCat> subcat { get; }
}
// add explicit interface implemetantion to existing 3 classes
// e.g.
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return subsubcat; } }
// IEnumerable<ITreeCat> ITreeCat.subcat { get { return Enumerable.Empty<ITreeCat>(); } }
IEnumerable<string> MyFunc(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
return (from cat in categories
where cat.id == idToMatch
select prefix + cat.name)
.Concat(from cat in categories
from recursiveResult in MyFunc(cat.subcat, idToMatch, prefix + cat.name + " > ")
select recursiveResult);
}
IEnumerable<string> MyFunc2(IEnumerable<ITreeCat> categories, string idToMatch, string prefix = "")
{
return categories.Where(cat => cat.id == idToMatch)
.Select(cat => prefix + cat.name)
.Concat(categories.SelectMany(cat => MyFunc2(cat.subcat, idToMatch, prefix + cat.name + " > ")));
}
This has the advantage that it continues to work if you later add a subsubsubcat etc.
All of the above code examples use a breadth-first search, and repeatedly enumerate the "parent" categories each time they go one level deeper.
In some applications a depth-first search may be a better choice, as each list is only enumerated once, in which case it's much easier to use foreach rather than Linq. Again, a recursive version is more concise than 3 nested loops with different classes:
IEnumerable<string> MyFuncDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
foreach(var cat in categories)
{
if (cat.id == idToMatch)
yield return cat.name;
foreach (var subResult in MyFuncDepthFirst(cat.subcat, idToMatch))
yield return string.Format("{0} > {1}", cat.name, subResult);
}
}
This still assumes that multiple matches can occur. If we're just after the first match, then there's no need to use an iterator block at all, and the above function can be modified to return a simple string:
string FirstMatchingIdDepthFirst(IEnumerable<ITreeCat> categories, string idToMatch)
{
foreach(var cat in categories)
{
if (cat.id == idToMatch)
return cat.name;
string subResult = FirstMatchingIdDepthFirst(cat.subcat, idToMatch);
if(subResult != null)
return string.Format("{0} > {1}", cat.name, subResult);
}
return null;
}
var list = this.AllCat.Where(t=>t.subcat.Any(s=> subsubcat.contains(s));
You can go for a method like below
public static Type Find(string id, MainCat m)
{
if (m.id.Equals(id))
{
return m.GetType();
}
if (m.subcat.Any(a => a.id.Equals(id)))
{
return typeof(subcat);
}
if (m.subcat.Any(a => a.subsubcat.Any(b => b.id.Equals(id))))
{
return typeof(subsubcat);
}
return null;
}
and perform the search. Find the gist, https://gist.github.com/IshamMohamed/33d75064789d77d88404b8ffc9a17e94
In this way you can increase the number of inner lists (eg: subsubsubcat)
Related
I have a Product table in my DB. Also, I have Brand and Category tables in my DB which are not related to each other. I want to relate these. In the form UI when I click the one of the Categories, should come the Brands which they have products in the related category.
I tried this way to do this. First, I get my products by categoryID with GetList method then I get these products' brands and I added these brands to pblist list(Brand type). However, some products have the same brands and pblist have repeated brand names. I tried to fix this with contains method but it does not work. Also, I have the same problem in the other part which I try to remove brands not included in pblist from blist(all brands' list). I tried removing item from blist by taking its index with this code: blist.RemoveAt(blist.IndexOf(item)); but this one also not working.It returns -1. But item is in the blist.
public class BrandVM : BaseVM
{
public int ProductCount { get; set; }
}
public class BaseVM
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return this.Name;
}
public class BrandService : ServiceBase, IBrandService
{
public List<BrandVM> GetList(int Count)
{
try
{
var result = GetQuery();
result = Count > 0 ? result.Take(Count) : result;
return result.ToList();
}
catch (Exception ex)
{
return null;
}
}
public List<BrandVM> GetListByCatID(int pCatID)
{
var plist = productService.GetListByCatID(pCatID);
List<BrandVM> pblist = new List<BrandVM>();
foreach (var item in plist)
{
if (!pblist.Contains(item.Brand))
{
pblist.Add(item.Brand);
}
};
var blist = GetList(0);
var blistBackup = GetList(0);
foreach (BrandVM item in blistBackup)
{
if (!pblist.Contains(item))
{
blist.Remove(item);
}
};
return blist;
}
These are my classes related to Brand. In BrandService I shared the filled methods there are more methods to fill.
This is method is in my ProductService:
I use that method to pull product list by CategoryID (plist)
public List<ProductVM> GetListByCatID(int EntityID)
{
try
{
var result = GetQuery().Where(x => x.Category.ID==EntityID);
return result.ToList();
}
catch (Exception ex)
{
return null;
}
}
This GetQuery method for ProductService, in other services there are some differences but there are similar
private IQueryable<ProductVM> GetQuery()
{
return from p in DB.Products
select new ProductVM
{
ID = p.ProductID,
Name = p.ProductName,
UnitPrice = (decimal)p.UnitPrice,
Category =p.CategoryID==null?null:new CategoryVM()
{
ID = (int)p.CategoryID,
Name = p.Category.CategoryName
},
Brand = p.BrandID == null ? null :
new BrandVM
{
ID=(int)p.BrandID,
Name=p.Brand.BrandName,
}
};
}
Entity framework will translate Linq queries into SQL statements, which means that Equals (and GetHashCode) will not be used for comparison of database objects. However, if you're comparing local instances of these objects, then these methods will be used for comparisons.
The default Equals does a reference comparison to determine equality, which literally means that two instances of a type are only considered equal if they both refer to the exact same object in memory.
Instead, we want to use the ID property for equality comparison, which means we need to override the Equals (and GetHashCode) methods for the class.
Here's an example of how you could do this:
public class BaseVM
{
public int ID { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Name;
}
public override bool Equals(object obj)
{
return obj is BaseVM &&
((BaseVM) obj).ID == ID;
}
public override int GetHashCode()
{
return ID;
}
}
Alternatively, if you don't want to modify the class (which I would recommend since it solves this problem everywhere), you can modify your code to filter out any brands that have the same id (or name):
foreach (var item in plist)
{
// Note: you could potentially use 'Name' instead of 'Id'
if (!pblist.Any(productBrand => productBrand.Id == item.Brand.Id))
{
pblist.Add(item.Brand);
}
}
Since you don't ensure that two different instances for a same brand are not equal,
in the sense that ´.Equals(object other)´ returns true,
the ´.Contains´ method as no way to identify them.
I think you'ĺl solve you issue by overriding .Equals in you Brand class.
I am wondering if there is some clever way to retrieve data from an enumerable using LINQ when individual values from multiple records are needed.
For example, let's say you have a person with three different phone fields:
public class Person
{
public Phone HomePhone { get; set; }
public Phone WorkPhone { get; set; }
public Phone CellPhone { get; set; }
}
...but the phone list is stored in a normalized format:
public enum PhoneType
{
Home, Work, Cell
}
public class Phone
{
public PhoneType Type { get; set; }
public string Number { get; set; }
}
static public IEnumerable<Phone> GetPhoneList()
{
yield return new Phone { Type = PhoneType.Home, Number = "8005551212" };
yield return new Phone { Type = PhoneType.Work, Number = "8005551313" };
yield return new Phone { Type = PhoneType.Cell, Number = "8005551414" };
}
If you needed to populate Person, you could write a loop, and get everything you need in one pass:
public static Person GetPerson1()
{
var result = new Person();
foreach (var ph in GetPhoneList())
{
switch (ph.Type)
{
case PhoneType.Home: result.HomePhone = ph; break;
case PhoneType.Work: result.WorkPhone = ph; break;
case PhoneType.Cell: result.CellPhone = ph; break;
}
}
return result;
}
But if you wanted to use LINQ, it seems like three passes may be needed:
public static Person GetPerson2()
{
return new Person
{
HomePhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Home ),
WorkPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Work ),
CellPhone = GetPhoneList().Single( ph => ph.Type == PhoneType.Cell )
};
}
Is there a clever way to use LINQ to get it all with only one pass over the enumeration?
Here is a link to a Fiddle if you'd like to explore my code.
(I am aware I could use a dictionary or other data structure to solve this particular problem; this is just an example.)
Normally, you can't do this in LINQ.
If you really want to, you can create a Foreach extension method and do the same as your GetPerson1 method.
public static class Ext
{
public static void Foreach<T>(this IEnumerable<T> e, Action<T> action)
{
foreach (T item in e)
{
action(item);
}
}
}
and then
public static Person GetPerson2()
{
var p = new Person();
var pl = GetPhoneList();
pl.Foreach(ph =>
{
switch (ph.Type)
{
case PhoneType.Home: p.HomePhone = ph; break;
case PhoneType.Work: p.WorkPhone = ph; break;
case PhoneType.Cell: p.CellPhone = ph; break;
}
});
return p;
}
But you really shouldn't. LINQ is meant to operate on IEnumerables (item by item), and LINQ functions should be without side effects, while your foreach loop and Foreach extension methods are only creating side effects, changing the state of the Person object.
And, besides, the fact that you need a 'clever way' should be an indication that this is not the way it's meant to be used :)
There's a great article by Eric Lippert with more details here: https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/
If there is no guarantee that numbers from the same person come in a sequence then you have to enumerate the list until you find all the numbers. It does not seem to me this is a good candidate for LINQ, whose purpose is to make the code more readable. Your foreach is just fine, and I would just break the loop when all numbers are found.
If you want to enumerate all the persons, and not just one then Dictionary approach is probably most effective. GroupBy internally uses a dictionary and you can use GroupBy to collect all the numbers belonging to a person, and then Aggregate to make a Person out of them. Let's assume there is some property Phone.PersonID, and also Person.PersonID, then you would have something like this:
GetPhoneList()
.GroupBy(x => x.PersonID)
.Select(x => x.Aggregate(new Person() { PersonID = x.Key },
(person, phone) =>
{
switch (phone.Type)
{
case PhoneType.Home: person.HomePhone = phone; break;
case PhoneType.Work: person.WorkPhone = phone; break;
case PhoneType.Cell: person.CellPhone = phone; break;
}
return person;
}));
I assume here that GetPhoneList() returns all the phones of all persons.
I have a class as follows:
public class Feature
{
public string Name { get; set; }
public string DisplayName { get; set; }
public List<Feature> SubFeatures { get; set; } = new List<Feature>();
}
I then have a List<Feature> features = new List<Feature>; where I store all my features.
Now, I'd like to know if a particular Feature (by name) exists in my features variable.
However, it can exist at any level (SubFeature of the SubFeature of the SubFeature for example).
The closest I've gotten was this:
public bool FeatureExists(Feature feature, string name)
{
return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result);
}
But it involves having to use a for on the caller of FeatureExists() to pass one top level feature at a time to it.
I'm sure there's an easier way to do this, how can I do this properly?
Define a recursive method like this:
public IEnumerable<Feature> FeatureAndSubFeatures(Feature feature)
{
yield return feature;
foreach (var subFeature in feature.SubFeatures)
{
foreach (var child in FeatureAndSubFeatures(subFeature))
{
yield return child;
}
}
}
Then use it:
FeatureAndSubFeatures(feature).Any(x => x.Name == name);
Another option is to put this method on Feature itself, called something like SelfAndSubFeaturesRecursive().
This approach - writing a method to recursively flatten the tree, rather than writing a specific method to search for a Feature with the given name - is flexible, as you can use it to search the tree for any node based on any criterion, or any subset of nodes, rather than being specialised for just finding nodes with a particular name.
You can also write it to take a collection of features to start with. Something like:
public IEnumerable<Feature> FeaturesAndSubFeatures(IEnumerable<Feature> features)
{
foreach (var feature in features)
{
yield return feature;
foreach (var child in FeaturesAndSubFeatures(feature.SubFeatures))
{
yield return child;
}
}
}
This is only useful if you're always starting with a collection of features, but saves a SelectMany in the event that you do.
I try to avoid recursion whenever possible. This is a version without:
public bool FeatureExists(Feature feature, string name)
{
var featureQueue = new Queue<Feature>();
featureQueue.Enqueue(feature);
while (featureQueue.Count > 0)
{
Feature current = featureQueue.Dequeue();
if (feature.Name == name)
return true;
foreach (Feature f in current.SubFeatures)
featureQueue.Enqueue(f);
}
return false;
}
If you find this less readable as commented you can use a generic extension method and use it whenever you need recursive checks, for example:
public static class Extensions
{
public static bool RecursiveCheck<T>(this T rootItem, Func<T, IEnumerable<T>> getChildrenFunc, Func<T, bool> predicate)
{
var queue = new Queue<T>();
queue.Enqueue(rootItem);
while (queue.Count > 0)
{
T current = queue.Dequeue();
if (predicate(current))
return true;
foreach (T child in getChildrenFunc(current))
queue.Enqueue(child);
}
return false;
}
}
Test-Feature:
Feature f1 = new Feature
{
Name = "1", SubFeatures = new List<Feature> { new Feature {Name="1.1", SubFeatures = new List<Feature> {new Feature {Name= "thename" } } }}
};
This simple one-liner remains:
bool containsName = f1.RecursiveCheck<Feature>(f => f.SubFeatures, f => f.Name == "thename");
You want to make a separate recursive method that does this for you.'
Try this:
public bool FeatureExists(Feature feature, string name) {
if(feature.Name == name) {
return true;
}
if(!feature.SubFeatures.isEmpty) {
foreach(Feature subFeature in feature.SubFeatures){
FeatureExists(subFeature, name)
}
}
return false;
}
the following should recurse through all the lists and populate "result" with all the possible features contained in the entire hierarchy
public class Feature
{
public string Name { get; set; }
public string DisplayName { get; set; }
public List<Feature> SubFeatures { get; set; } = new List<Feature>();
}
other class
List<feature> result = new List<feature>();
public void FindItems(Feature yourFeature)
{
result.add(yourFeature);
foreach(Feature feature in yourFeature)
{
if(feature.SubFeatures.count != 0)
{
foreach(Feature subfeature in feature)
{
FindItems(subfeature);
}
}
else
{
result.add(feature);
}
}
}
Well I'm not sure if this is what you are asking but if you want cleaner code you may write your function as an extension method and use LINQ instead of for loops to get cleaner code.
public static bool FeatureExists(this Feature feature, string name)
{
return feature.Name == name || feature.SubFeatures.Select(subFeature => FeatureExists(subFeature, name)).Any(result => result);
}
And then
List<Feature> mainFeatures = new List<Feature>();
mainFeatures.Any(obj => obj.FeatureExists("abc"));
If you want event shorter and cleaner code you might consider having a feature as a parent of all features like a mother feature and then call your recursive method over that.
But anyway consider making your method an extension method.
I'm doing a SAT Solver (mainly the DPLL or Partial DPLL) and I have the method for Unit Propogation. Basically what it does is that it checks whether there are any standalone literals, and removes that literal, and any instance found in the other clauses. Any Example would be
(x) (x,y) (w,z)
the Unit Propogation would be 'x' and when performing the unit prop it would leave only (w,z)
In this method I have several nested foreach loops and List<literals> <literals> is a custom made class which has 2 variables hasNegation (bool) and char literalCharacter
The Coding is below, and will explain from below
foreach (clauses c1 in listOfClauses)
{
if (c1.listOfLiterals.Count == 1)
{
literals l1 = c1.listOfLiterals[0];
solved.Add(l1);
foreach (clauses c2 in listOfClauses)
{
List<literals> tempList = new List<literals>();
foreach (literals l2 in listOfLiterals)
{
if (l2.solveDPLL(l1))
{
removable.Add(c2);
}
else
{
if (c2.listOfLiterals.Count == 1)
{
UNSAT = true;
return false;
}
else
{
if (l1.solveDPLL(l2))
{
tempList.Add(l2);
}
}
}
c2.listOfLiterals.RemoveAll(tempList); //obviously giving error
}
}
}
}
return true;
}
I have 2 List <literals> which are templist and listOfLiterals where the LATTER is the "parent"
I am tryign to remove the entries of listOfLiterals that match with tempList and I use c2.listOfLiterals.RemoveAll(tempList); obviously will output an error as it is not a Delegate.
I've searched a lot,even on stackoverflow, but every one of them compares either to an ID or an integer. In my case, since I'm just comparing 2 Lists, how can I do the delegate so that, the entries that are the same in both listOfLiterals and tempList are removed from listOfLiterals?
Many thanks
EDIT:
Literals Class
public class literals
{
public char literalCharacter { get; set; }
public bool negation { get; set; }
public literals(char lc, bool neg )
{
literalCharacter = lc;
negation = neg;
}
public bool solveDPLL (literals lit)
{
return ((Object.Equals(literalCharacter, lit.literalCharacter) && (negation == lit.negation)));
}
public String toString()
{
return literalCharacter + " : " + !negation;
}
}
If you're okay with using a little LINQ magic:
c2.listOfLiterals = c2.listOfLiterals.Except(tempList).ToList();
Or loop over the tempList:
foreach (var item in tempList)
{
c2.listOfLiterals.Remove(item);
}
You may need your literals class to implement IEqualityComparer<literal> and then provide an implementation for Equals and GetHashCode. See the MSDN page for Except for a good example of this.
I have a List<Category> where Category is:
public class Category {
public List<Category> Categories { get; set; } // this holds sub-categories
public string Name { get; set; }
public string Icon { get; set; }
public string Id { get; set; }
}
Since Categories is itself another List<Category> it could contain sub-categories, and those sub-categories could contain sub-categories, and so forth...
I know I can query past the first "layer" like so:
Categories.Where(x => x.Categories.Any(c => c.Id == id)).FirstOrDefault();
How can I effectively query for a specific Category by Id, perhaps 3,4, or 5 layers deep in the object tree (there are at most 3, but for future reference I'd like to know)?
Edit
In addition, how could I get the entire object tree, all the way up to the top level Category, if I only had an Id of a sub-category 3 layers deep?
This will recursively traverse categories until category matching passed id will be found (if any). Full path to found category will be returned (i.e. like breadcrumbs menu does):
static IEnumerable<Category> GetById(IEnumerable<Category> categories, string id)
{
if (categories == null || !categories.Any())
yield break;
Category result = categories.FirstOrDefault(c => c.Id == id);
if (result != null)
{
yield return result;
yield break;
}
foreach (var category in categories)
{
var subCategories = GetById(category.Categories, id);
if (subCategories.Any()) // we have found the category
{
yield return category; // return current category first
foreach (var subCategory in subCategories)
yield return subCategory;
yield break; // don't search in other categories
}
}
}
Usage:
IEnumerable<Category> result = GetById(categories, id);
// Food > Beer > Kilkenny
string breadcrumbs = String.Join(" > ", result.Select(c => c.Name).ToArray());
You can convert this method to extension if you wish.
You could write an extension method like the following to flatten Category as an IEnumerable<Category>:
public static IEnumerable<Category> Flatten(this Category category)
{
if (category.Categories != null)
{
foreach (var sub in category.Categories)
{
foreach (var subSub in sub.Flatten())
yield return subSub;
}
}
yield return category;
}
Then you use Linq on the IEnumerable<Category> as you like:
var filtered = categoryList.SelectMany(x => x.Flatten())
.Where(x => x.Id == id);
You would need to recurse through the Categories if you had an indeterminate level of nesting, and even if you had a fixed level of nesting, for any level of nesting more than 2-3 levels down, it would be worth recursing.
Linq doesn't really have a way of expressing recursion, although this post talks about using Linq2Xml features to achieve it: Expressing recursion in LINQ
If you are able to modify the class itself, you could implement a GetChildById-style method to recursively scan through the child Categories.