I have a base class:
public abstract class BaseClass{
public bool IsSelected {get; set;}
}
A derived class with a collection representing a hierarchy:
public class DerivedOne : BaseClass{
public ObservableCollection<BaseClass> Children {get; set;}
}
Another derived class:
public class DerivedTwo : BaseClass{
}
What is the simplest way to find all of the elements under a DerivedOne root that have IsSelected set to true?
You left out some requirement detail, but I think something like this should work:
public IEnumerable<BaseClass> AllIsSelected(BaseClass root)
{
if (root.IsSelected)
{
yield return root;
}
var composite = root as DerivedOne;
if (composite != null)
{
foreach (var v in composite.Children)
{
foreach (var x in AllIsSelected(v))
{
yield return x;
}
}
}
}
Of course, if you want a full list all at once, you could build the list instead of using 'yield'.
This is the same design discussed here: IEnumerable and Recursion using yield return.
As another answer said, you can use LINQ to shorten this somewhat. This version avoids making the temporary list.
public IEnumerable<BaseClass> AllIsSelected(BaseClass root)
{
if (root.IsSelected)
{
yield return root;
}
var composite = root as DerivedOne;
if (composite != null)
{
foreach (var x in composite.Children.SelectMany(v => AllIsSelected(v)))
{
yield return x;
}
}
}
The simplest method would be to use LINQ with recursion
public IEnumerable<BaseClass> GetAllSelectedChildren(DerivedOne derivedOne)
{
return derivedOne.Children.SelectMany(GetAllSelected);
}
public IEnumerable<BaseClass> GetAllSelected(BaseClass baseClass)
{
var selected = new List<BaseClass>();
if(baseClass.IsSelected)
{
selected.Add(baseClass);
}
var derivedOne = baseClass as DerivedOne;
if(derivedOne != null)
{
selected.AddRange(GetAllSelectedChildren(derivedOne));
}
return selected;
}
Use simple Linq.
return root.Children
.SelectMany(c => new[]{ c }.Concat(c.Children)) // flatten the structure including self node.
.Where(e => e.IsSelected) // filter selected items
.ToList();
Related
I'm currently working with a tree structure defined like this
public class TreeNode
{
private ObservableCollection<TreeItem> nodeItems;
private ObservableCollection<TreeNode> nodeChildren;
//public "NodeItems" and "NodeChildren" getters and setters
}
public class TreeItem
{
private bool isSelected;
private string Name;
//public "IsSelected" and "Name" getters and setters
}
public class Tree
{
private TreeNode rootNode;
//public getters and setters properties
}
and I'm trying to write a function or a public property that recursively gets all the nodeItems in the Tree that have isSelected == true and make it a flat collection.
So I wrote this function in the TreeNode class, that recursively navigates through the children:
public ObservableCollection<TreeItem> SelectedItems()
{
ObservableCollection<TreeItem> tempCollection = new ObservableCollection<TreeItem>();
if (nodeItems != null)
{
foreach (TreeItem item in nodeItems)
{
if (item.IsSelected == true)
{
tempCollection.Add(item);
}
}
}
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
tempCollection.Concat(node.SelectedItem());
}
}
return tempCollection;
}
but it always returns an empty collection at the end.
How can I correct it, and maybe improve it (by using a Lambda expression or a property)?
The Concat function on the ObservableCollection does not modify any of the arguments. You have to assign the resulting object to your tempCollection.
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
tempCollection = new ObservableCollection<TreeNode>(tempCollection.Concat(node.SelectedItem()));
}
}
EDIT: Alternatively, you can use an overloaded private method to not use so many temporary collections:
public ObservableCollection<TreeItem> SelectedItems()
{
ObservableCollection<TreeItem> toReturn = new ObservableCollection<TreeItem>();
SelectedItems(toReturn);
return toReturn;
}
private void SelectedItems(ObservableCollection<TreeItem> tempCollection)
{
if (nodeItems != null)
{
foreach (TreeItem item in nodeItems)
{
if (item.IsSelected == true)
{
tempCollection.Add(item);
}
}
}
if (nodeChildren != null)
{
foreach (TreeNode node in nodeChildren)
{
node.SelectedItems(tempCollection);
}
}
}
You can simplify your definition of a tree down to this:
public class Tree : ObservableCollection<Tree>
{
public ObservableCollection<TreeItem> nodeItems;
}
Now you can do this:
public IEnumerable<TreeItem> FlattenIsSelected(Tree tree)
{
return tree.nodeItems.Where(x => x.isSelected)
.Concat(tree.SelectMany(t => FlattenIsSelected(t)));
}
It's not much more difficult if you keep your current definitions.
I made a pattern which i dont really like.
It is as the following:
List<Element> listOfPossibleResults = getAllPossibleResults();
Element result = findResult(getFirstPriorityElements(listOfPossibleResults));
if (result!= null)
{
return result;
}
result = findResult(getSecondPriorityElements(listOfPossibleResults));
if (result!= null)
{
return result;
}
private Element findResult(List<Element> elements) {...};
private List<Element> getFirstPriorityElements(List<Element> elements) {...};
private List<Element> getSecondPriorityElements(List<Element> elements) {...};
etc..
Basically i'am creating sublists based on a couple of rules. After creating the sublist, i try and find a specific element in it. If i dont find, i move on to the next priority, and so on.
I would like a solution where i can iterate over these criterias, until i find a solution. But i dont know how to get them to a format which i can iterate over.
Can you guys give me a C# specific solution of the issue?
As #Lepijohnny mentioned, you can use Chain of responsibility design pattern. For example:
abstract class Handler<TRequest, TResult>
{
protected Handler<TRequest, TResult> successor;
public void SetSuccessor(Handler<TRequest, TResult> successor)
{
this.successor = successor;
}
public abstract TResult HandleRequest(TRequest request);
}
class FirstHandler : Handler<List<Element>, Element>
{
public override void HandleRequest(TRequest request)
{
Element result = findResult(getFirstPriorityElements(request));
if (result == null)
{
result = sucessor?.HandleRequest(request);
}
return result;
}
private Element findResult(List<Element> elements) {...};
private List<Element> getFirstPriorityElements(List<Element> elements) {...};
}
class SecondHandler : Handler<List<Element>, Element>
{
public override void HandleRequest(TRequest request)
{
Element result = findResult(getSecondPriorityElements(request));
if (result == null)
{
result = sucessor?.HandleRequest(request);
}
return result;
}
private Element findResult(List<Element> elements) {...};
private List<Element> getSecondPriorityElements(List<Element> elements) {...};
}
Usage:
void Example()
{
// Setup Chain of Responsibility
var h1 = new FirstHandler();
var h2 = new SecondHandler();
h1.SetSuccessor(h2);
var result = h1.Handle(new List<Element>());
}
It's a just quick example. I think it describe how this pattern works and you will be able to adjust it for your needs.
In the "result" class put a property called "Priority (int)" then:
result = listOfPossibleResults.GroupBy(x => x.Priority).OrderBy(x => x.Key);
then:
return result.FirstOrDefault(x => x.Count() > 0);
You will need to fill in the priority of the result items when you first retrieve them.
P.S. I typed the code right here, forgive me if there is a spelling mistake somewhere.
If you could refactor the methods getFirstPriorityElements(List<> list) to a single getPriorityElements(List<> list, int nr) you could do the following
method IteratePredicates(List<> list, int nr = 0)
{
if (nr>maxpriority) return null;
return findresult(getPriorityElements(list,nr)) ?? IteratePredicates(list,nr++);
}
In a for loop:
method IteratePredicates(List<> list, int nr = 0)
{
for (int i = 0; i < maxpriority; i++)
{
var result = findresult(getPriorityElements(list, nr));
if (result != null)
return result;
}
return null;
}
Am I right that your get__PriorityElements is literally a filter? In that case, it's more declarative and hopefully more readable to treat those like this:
Func<Element, bool> isFirstPriority = ...;
var firstPriorityElements = elements.Where(isFirstPriority);
And now your overall goal is to extract a single element (or none) from the highest-possible priority subsequence, using a predicate contained in findResult? So replace this with an actual predicate
Func<Element, bool> isResult = ...;
like so. Now you want to look through all the first priority elements for an isResult match, then if not found all the second priority elements, etc. This sounds just like a sequence concatenation! So we end up with
var prioritisedSequence = elements
.Where(isFirstPriority)
.Concat(elements
.Where(isSecondPriority))
.Concat....;
And finally the result
var result = prioritisedSequence
.FirstOrDefault(isResult);
Since Where and Concat are lazily enumerated this has the benefit that it is declarative while avoiding more work than necessary, and it's lightweight and 'LINQy' as well.
If you want abstract it even more, and anticipate changes in how priorities will be arranged, you could actually make a higher order list for those like this:
IEnumerable<Func<Element, bool>> priorityFilters = new[]
{
isFirstPriority,
isSecondPriority,
...
};
and then the concatenation can be performed as an aggregation over that sequence:
var prioritisedSequence = priorityFilters
.Aggregate(
Enumerable.Empty<Element>(),
(current, filter) => current.Concat(elements.Where(filter)));
This change may make it easier to add new priorities in future, or you may think it clutters and hides the intention of your code.
You can treat methods as objects using Func<T, T>, and then you can also put them in e.g. an array. Then you can iterate over the array, calling the methods one by one until a result is found.
The solution then becomes:
var methods = new Func<List<Element>, List<Element>>[]
{ getFirstPriorityElements, getSecondPriorityElements };
return methods
.Select(method => findResult(method(listOfPossibleResults)))
.Where(result => result != null)
.FirstOrDefault();
This is short and readable, works without changing your methods or types, and no need to add classes just for the sake of applying a pattern.
You can use the specs pattern Here is a sample code:
Create a interface with a criteria:
public interface ISpecification<T>
{
Expression<Func<T, bool>> Criteria { get; }
}
Then create a class that holds your query specs:
public class GlobalSongSpecification : ISpecification<Song>
{
public List<int> GenreIdsToInclude { get; set; } = new List<int>();
public List<int> AlbumIdsToInclude { get; set; } = new List<int>();
public List<string> ArtistsToInclude { get; set; } = new List<string>();
public string TitleFilter { get; set; }
public int MinRating { get; set; }
[JsonIgnore]
public Expression<Func<Song, bool>> Criteria
{
get
{
return s =>
(!GenreIdsToInclude.Any() || s.Genres.Any(g => GenreIdsToInclude.Any(gId => gId == g.Id))) &&
(!AlbumIdsToInclude.Any() || AlbumIdsToInclude.Contains(s.AlbumId)) &&
(!ArtistsToInclude.Any() ||ArtistsToInclude.Contains(s.Artist)) &&
(String.IsNullOrEmpty(this.TitleFilter) || s.Title.Contains(TitleFilter)) &&
s.Rating >= MinRating;
}
}
}
Create a repository with a method that exposes one that receives ISpecification:
public interface ISongRepository
{
IEnumerable<Song> List(ISpecification<Song> specification);
//IQueryable<Song> List();
Song GetById(int id);
void Add(Song song);
IEnumerable<string> AllArtists();
IEnumerable<Genre> AllGenres();
}
And your client code call the GlobalSongSpecification, populates it and pass it to the repository in order to filter by the criteria:
public ActionResult Index(List<int> selectedGenres = null,
List<string> selectedArtists = null,
string titleSearch = null,
int minRating = 0,
string filter = null,
string save = null,
string playlistName = null)
{
if (selectedArtists == null) { selectedArtists = new List<string>(); }
if (selectedGenres == null) { selectedGenres = new List<int>(); }
var spec = new GlobalSongSpecification();
spec.ArtistsToInclude.AddRange(selectedArtists);
spec.GenreIdsToInclude.AddRange(selectedGenres);
spec.MinRating = minRating;
spec.TitleFilter = titleSearch;
var songs = _songRepository.List(spec);
//You can work with the filtered data at this point
}
And you populate a razor view or expose it as web api.
The sample code is from pluralsight desing patterns library course Here(Specification Pattern module)
I have created a class which inherits from List: Now I want to iterate the elements of the class in the class itself and return the object if it satisfies certain criteria (e.g. its Property name):
protected class myList : List<Object>
{
getElement(string name)
{
??? LOOP AND IF CONDITION ???
return element;
}
}
Can you help? Thanks
You're looking for foreach(var item in this).
First, I would try to avoid using List<Object>.The problem with Object is, that it needs casting. Hence it is prone to errors at runtime.
Rather create your own class.
For example, a simple item, which holds a name.
class Item
{
public string Name { get; set; }
}
Then you can simply do:
class myList : List<Item>
{
Item getItem(string name)
{
foreach(var item in this)
{
if(item.Name==name) { return item; }
}
return null;
}
}
You'd want some more code for the casting, but:
public Object GetElement(string name)
{
Object element = null;
foreach (var item in this)
{
if (item.ToString() == name)
{
element = item;
break;
}
}
return element;
}
You don't have to reinvent the wheel, just use LINQ.
mylist.First(element => /*criteria*/);
Lets say I have a List<Family> and each Family has a List<Child>.
When I encounter a Child object in my code, how can I determine if the child is a part of any family?
Pseudo code:
If Child not in any family
// Do something with child
Update:
Example models:
class Family
{
public List<Child> Children {get;set;}
// Properties
}
class Child
{
// Properties
}
Example ViewModel:
class FamilyViewModel
{
public List<Family> Families {get;set;}
public bool ChildHasFamily(Child child)
{
// Determine if child is in any family or not
}
}
Now this isn't all that clean to me. I think your Child should have Family property to make things easier. If I understand correctly, your view model has a list of family which in turn contains a list of childs. When you receive a list of child, you want to know if it is in one of your family:
class FamilyViewModel
{
public List<Family> Families {get;set;}
public void ChildHasFamily(Child child)
{
var hasFamily = Families.SelectMany(f => f.Children)
.Contains(child);
}
}
Note that this will do an object reference comparison. If Child implement IEquatable<Child>, it will work out of the box. If not, you can use:
class FamilyViewModel
{
public List<Family> Families {get;set;}
public void ChildHasFamily(Child child)
{
var hasFamily = Families.SelectMany(f => f.Children)
.Any(c => c.Name == child.Name);
}
}
Replace the Where predicate for your identity comparison.
You could use following recursive Traverse method which uses deferred execution.
Then it's easy as:
IEnumerable<Family> familiesOfChild = families.Traverse(f => f.Children)
.Where(c => c.Equals(yourChild));
if(!familiesOfChild.Any())
{
// oh, what a poor child
}
Here is the extension method:
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
{
foreach (T item in source)
{
yield return item;
IEnumerable<T> seqRecurse = fnRecurse(item);
if (seqRecurse != null)
{
foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
{
yield return itemRecurse;
}
}
}
}
I have a custom class called CustomClass. It contains a variable called "Name" and a list of values (for the sake of simplicity let's make this an int - in reality it is another custom class, but the principle should be the same).
So :
public class CustomClass {
string name;
}
I have a List<CustomClass>.
When I attempt to add a value to this List, the logic I want, is for this List to check if it contains a CustomClass with the same name as the value I want to add.
If it does, then do x, otherwise, do y.
listOfCustomClass.Contains(customClassToAdd.name) will not work in this case, I assume, however this is the functionality I require.
What is best practice here ?
I think you can try something like var x = MyList.Where(C=> C.Name == InsertedName) and check the result (not tested)
You'll have to create a new class,let's call it CustomList, that inherits from IList<> where you can override the add method, do your check, and then add it to the base. Something like this:
public class CustomList<T> : IList<T> where T : CustomClass
{
private List<T> innerlist;
public void Add(T item)
{
if(innerlist.Any(a => a.Name == item.Name)))
innerlist.Add(item);
}
}
you can do it using linq as follow but you have to make name field public.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Any(p => p.name.Equals(toCheck)))
{
//do x here
}
else
{
//do y here
}
however if you don't want to use linq then Do some changes in CustomClass as follow
public class CustomClass
{
string name;
List<int> intLost = new List<int>();
public override bool Equals(object obj)
{
return this.Equals(obj as CustomClass);
}
public override int GetHashCode()
{
return 0;
}
public bool Equals(CustomClass cc)
{
if (cc == null) return false;
return this.name.Equals(cc.name);
}
}
Then you can do this.
List<CustomClass> list = new List<CustomClass>();
CustomClass toCheck = new CustomClass();
if (list.Contains(toCheck))
{
//do x here
}
else
{
//do y here
}
It seems to me that you want to override the .Add() behavior of your List<CustomClass>. While you could use extension methods, I think a better solution would be to invent a class that extends List in some manner. I'd recommend implementing IList in your collection class if you need to have that level of control over add operations...
public class CustomClassList : IList<CustomClass>
{
public void Add (CustomClass item)
{
if(this.Select(t => t.Name).Contains(item.Name))
// Do whatever here...
else
// Do whatever else here...
}
// ... other IList implementations here ...
}
try this:
IList<CustomClass> list = new List<CustomClass>();
CustomClass customClass = new CustomClass();
customClass.name = "Lucas";
if((list.Tolist().Find(x => x.name == customClass.name)) == null)
{
list.Add(customClass);
}
else
{
//do y;
}
You could override the Equals(object o) function in your CustomClass, so that two CustomClasses are considered equal if their names are the same. Then
listOfCustomClass.Contains(customClassToAdd);
should work.
Another way is to override Equals method on your CustomClass and then just call List.Contains()
If the name property uniquely identifies the CustomClass, then you should overload Equals and GetHashCode(). The reason List.Contains doesn't work is that underneath the HashCodes are compared. So you need to overload GetHashCode and Equals something like this:
public override int GetHashCode()
{
return this.name.GetHashCode();
}
public override bool Equals(object obj)
{
var other = obj as CustomClass;
if (other != null)
{
if (other.Name == this.Name)
{
return true;
}
}
return false;
}