How to edit a property that is deep within an object - c#

Suppose there is a person object that has many ICollection
and ObjectType2 has Icollection
So to edit a property you could theoretically search deep with forloops.
But what would be a better way that is syntactically nice.
For example to edit a property called PP one could do the following:
foreach (var item in PersonData)
{
foreach (var item2 in item.Staffs)
{
foreach (var item3 in item2.Employees)
{
foreach (var item4 in item3.EmployeePositions)
{
item4.PP = "test";
}
}
}
}
But I am looking for something much nice such as:
Whether it via linq or whatever method.

var positions = PersonData
.SelectMany(p => p.Staffs)
.SelectMany(s => s.Employees)
.SelectMany(e => e.EmployeePositions);
foreach (var position in positions)
{
position.PP = "test";
}
This is the equivalent to the nested loops.

If these objects were stored in a database you would almost certainly do a query against the EmployeePositions table possibly filtering it by joining back against the Employees or Staff tables.
If you really need to access all instances of EmployeePositions you perhaps need a separate collection containing them rather than continually enumerating through the properties of other objects to find them.

Related

How to reduce multiple nested foreach blocks

I have the following scenario:
var Ids = object1.GetIds(); // returns IEnumerable<int>
foreach (var id in Ids)
{
foreach (var relatedObject in object1.GetRelatedObjects(id))
{
// Do Something with related object
}
}
In this case, i want to get rid of from the first foreach and reduce this logic into single foreach. How could i achieve this?
Should it be possible with LINQ expression some similar methodology?
When there is nothing between the two loops, before or after the nested one, you can use SelectMany to "flatten" two loops into one:
foreach (var relatedObject in Ids.SelectMany(object1.GetRelatedObjects)) {
...
}
One major difference between this loop and the loop that you have is that id is no longer in scope. Assuming that relatedObject exposes a public Id property, this should not be a problem in your situation, because you could extract the id back with
var id = relatedObject.Id;
Personally I like to take full advantage of the optional braces/block for foreach loops.
You can't reduce the complexity. But you can make it look nicer
IEnumerable<int> Ids = object1.GetIds()
foreach (var id in Ids)
foreach (var relatedObject in object1.GetRelatedObjects(id))
{
DoSomething(relatedObject);
}

Iterating a list of objects with foreach

I came across this statement:
"When using foreach on a list of objects, the iterated object instance is not editable, but the object properties are editable"
Could someone demonstrate the above with a simple example, please?
Let me re-phrase (as I found the statement in two versions), maybe this statement is more clear:
"When using foreach on a list of elements, the iteration variable that provides the element is readonly, but the element properties are editable"
foreach(var foo in foos)
{
foo = null; // WRONG, foo is not editable
foo.name = "John"; // RIGHT, foo properties are editable
}
What is means is that the items in the list can't change whilst iterating, but the contents of the items may.
this will alter the collection and prevent the foreach completing:
foreach(var item in collection)
{
collection.Remove(item);
}
This will change an item in the list and not prevent the foreach completing:
foreach(var item in collection)
{
item.name = "Neil";
}
Yes
Foreach (n in list) if (n.something==true) list.Remove(n);
this will fail
you cannot remove an item in list, unlike say a for loop
Not sure you need an example for this. You will step over each object in the collection, and you can do what you like to each of those objects, but you can't make changes to the collection itself, e.g. Insert, Remove, Clear, etc. Attempting such will throw an exception.
foreach var car in cars
{
//you can edit car.color here
//you cannot edit car
}

Define a condition in a loop, without add an if

I have a loop that is going through each element of a list.
At the moment I have the foreach loop, and inisde, an if that check a variable for the element of the list. If the value is true, I do certain activities, otherwise I move on.
I was wondering if there is some sort of construct, that would allow me to write something like
foreach (myclass item in itemlist) where ( item.is_red)
{
... do something
}
instead than what I have currently
foreach (myclass item in itemlist)
{
if (item.is_red)
... do something
}
From what I recall, the "where" can be used only as constant definition mechanism, like when defining a specific type of class; so it can't be used in the context that I am presenting here. Am I stuck with the if-else inside the loop as only solution?
Convert the enumeration to List and use the Where and ForEach LINQ operator:
itemlist.Where(t => t.is_red).ToList().ForEach(t => DoSomething(t));
or
itemlist.Where(t => t.is_red).ToList().ForEach(t =>
{
// do something
}
You could use LINQ
foreach (myclass item in itemlist.Where(i => i.is_red))
{
//... do something
}
Yes, you could filter what you want iterate with LINQ Where in the collection first:
foreach (myclass item in itemlist.Where(x => x.is_red)) //here is the `Where`
{
//do your stuffs
}
Almost :)
foreach (var item in (from i in numbers where i.is_red select i))
{
... do something
}
There generally is a small performance hit when LINQ is used to iterate over lists or arrays, since an enumerator object is to be created.
Another way to tackle your problem is to use your condition as guard clause. This can reduce nesting and complexity and usually improves readability (although there is also the opinion of not using multiple returns/continues/breaks in a function/loop to keep cyclomatic complexity down):
foreach (var item in itemlist)
{
if (!item.is_red) { continue; }
... do something
}
In the end it probably boils down to company policy/personal preference.

LINQ: Find if one item in List appears in a string array

I have a string array and another object that has a list of objects, on of the properties is a string.
public string[] allowedroles;
foreach (var role in allowedroles)
{
if (user.RolesList.Exists(r => r.Name == role))
{
authorize = true;
break; // if one match that break out of loop
}
}
Is there a way to perform with just a LINQ statement, without the foreach loop?
It sounds like you want:
var authorize = user.RolesList.Exists(r => allowedRoles.Contains(r.Name));
Or transform the list of roles into their names, and see whether that intersects with the allowed roles:
var authorize = user.RolesList.Select(r => r.Name).Intersect(allowedRoles).Any();
Note that you could use Any instead of Exists, which would stop it being specific to List<T>:
var authorize = user.RolesList.Any(r => allowedRoles.Contains(r.Name));
Also note that even though the foreach loop is no longer in the code, using LINQ won't make this code any faster - there's still a loop, it's just in the LINQ code rather than in your code. But hey, readability is still a big win :)

Linq .ForEach Optimization

Is there a more elegant and/or faster way of writing the following?
foreach(var listing in listings)
{
listing.Reviews.ForEach(r => r.ListingID = listing.ListingID);
listing.OpenHours.ForEach(o => o.ListingID = listing.ListingID);
listing.Photos.ForEach(r => r.ListingID = listing.ListingID);
listing.Types.ForEach(t => t.ListingID = listing.ListingID);
}
All it is doing is going through all the child collections and setting a property on each of the items in the child collections
If classes hidden under all these properties implement common interface which exposes ListingID property, you could do following:
foreach(var listing in listings)
{
// does not create new list or array! just prepares in-memory query
var items = listing.Reviews
.Concat<IEntity>(listing.OpenHours)
.Concat<IEntity>(listing.Photos)
.Concat<IEntity>(listing.Types);
foreach(var item in items)
item.ListingID = listing.ListingID;
}
Although, someone may find it a little bit more readable I don't expect it to be faster than your current approach.

Categories