Linq .ForEach Optimization - c#

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.

Related

How to edit a property that is deep within an object

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.

How can I simplify this LINQ code

Basically I have a multiselect list box in MVC and when the user changes selection it will come back and it should update the model. the below code works but I am just wondering how can I put it in a single foreach loop or is there a better way of updating the selection? Note: There is a many to many relationship between artist and artist type.
foreach (var artistTtype in this._db.ArtistTypes.ToList().Where(artistTtype => artist.ArtistTypes.Contains(artistTtype)))
{
artist.ArtistTypes.Remove(artistTtype);
}
foreach (var artistTtype in this._db.ArtistTypes.ToList().Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)))
{
artist.ArtistTypes.Add(artistTtype);
}
This for adding (just use AddRange):
artist.ArtistTypes.AddRange(this._db.ArtistTypes
.Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)));
This for removing (use ForEach):
this._db.ArtistTypes
.Where(artistTtype => artist.ArtistTypes.Contains(artistTtype)).ToList()
.ForEach(x=>artist.ArtistTypes.Remove(x));
EDIT:
you can always set
artist.ArtistTypes = this._db.ArtistTypes
.Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID)).ToList();
this will set ArtistTypes to what you want, you don't need to delete then add.
I see two "fixes":
1) You don't need to care about what's inside the list, since you're going to update the list of selections you can start from scratch, so the removal part becomes
artist.ArtistTypes.Clear();
2) Now you fill the list again. ToList() should not be needed since you're performing a .Where() to get the data, and you can leverage Linq's lazy mechanisms so you'll only read the data you actually use. You can also split the lines for increased readability (it doesn't matter: until you do the foreach() the db will not be actually hit.
//note that the .ToList() is gone
var query = this._db.ArtistTypes.Where(artisttype => vm.SelectedIds.Contains(artisttype.ArtistTypeID);
foreach (var artistTtype in query))
{
artist.ArtistTypes.Add(artistTtype);
}
2b) (UNTESTED, off the top of my head) Another way of implementing the comparison you do is through a custom IEqualityComparer, switching to .Intersect() method. This is way more solid since if your keys change in the model you only have to change the comparer.
// I'm making up "ArtistType", fix according to your actual code
class ArtistTypeEqualityComparer : IEqualityComparer<ArtistType>
{
public bool Equals(ArtistType x, ArtistType y)
{
if (ArtistType.ReferenceEquals(x, null)) return false;
if (ArtistType.ReferenceEquals(y, null)) return false;
if (ArtistType.ReferenceEquals(x, y)) return true;
return x.ArtistTypeId.Equals(y.ArtistTypeId);
}
public int GetHashCode(ArtistType obj)
{
return obj.ArtistTypeId.GetHashCode();
}
}
// And then the "add" part simplifies
artist.ArtistTypes.AddRange(this._db.ArtistTypes.Intersect(vm.SelectedIds.Select(x => new ArtistType{ ArtistTypeId = x }));

Efficient Hierarchal Linq query using multiple properties

I have a fairly large collection of foo { int id, int parentid, string name}.
I am looking to collect a list of foo objects where the object has a name of "bar3", and is a child of an object named "bar2", which is a child of an object with an ID of 1.
What sort of collection should I use (I've been playing with lookups and dictionaries with not a whole lot of success) and how should I write this to make an efficient function out of this? There are approximately 30K foo objects and my method is choking to death.
Thanks!
If I really had to stick with this layout for foo, and I really had to make lookups as fast as possible (I don't care about memory size, and will be reusing the same objects repeatedly, so the cost of setting up a set of large structures in memory would be worth it), then I would do:
var byNameAndParentLookup = fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name)); //will reuse this repeatedly
var results = byNameAndParentLookup[Tuple.Create(1, "bar2")].SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
That said, if I was going to store tree data in memory, I'd prefer to create a tree-structure, where each foo had a children collection (perhaps a dictionary keyed on name).
Edit: To explain a bit.
fooSource.ToLookup(f => Tuple.Create(f.parentid, f.name))
Goes through all the items in fooSource (wherever our foo objects are coming from), and for each one creates a tuple of the parentid and the name. This is used as a key for a lookup, so for each parentid-name combination we can retrieve 0 or more foo objects with that combo. (This will use the default string comparison, if you want something else such as case-insensitive, create an IEqualityComparer<Tuple<int, string>> implementation that does the comparison you want and use .ToLookup(f => Tuple.Create(f.parentid, f.name), new MyTupleComparer())).
The second line can be broken down into:
var partWayResults = byNameAndParentLookup[Tuple.Create(1, "bar2")];
var results = partWayResults.SelectMany(f => byNameAndParentLookup[Tuple.Create(f.id, "bar3")]);
The first line simply does a search on our lookup, so it returns an enumeration of those foo objects which have a parentid of 1 and a name of "bar2".
SelectMany takes each item of an enumeration or queryable, and computes an expression that returns an enumeration, which is then flattened into a single enumeration.
In other words, it works a bit like this:
public static SelectMany<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TResult>> func)
{
foreach(TSource item in source)
foreach(TResult producedItem in func(item))
yield return producedItem;
}
In our case, the expression passed through takes the id of the element found in the first lookup, and then looks for any elements that have that as their parentid and have the name "bar2".
Hence, for every item with parentid 1 and name bar2, we find every item with that first item's id as its parentid and the name bar3. Which is what was wanted.
Check this out: QuickGraph
I've never actually used it but it seems well documented.
Alternatively you can try the C5 Generic Collection Library
I got this from this tread
I can suggest you to group all items by parentId first then apply conditions on it. First you will need to find group with bar1 element, than you should select all its childs and try to find element with name bar 2...
I can suggest such solution, it not the best but it work (thirdLevelElements will contain needed elements). I've used foreachs to make it clear, this logic could be written in linq statements but for me it will be complicated to understand.
var items = new[]
{
new Foo{id=1,parentid = 0, name="bar1"},
new Foo{id=2,parentid = 1, name="bar2"},
new Foo{id=3,parentid = 2, name="bar3"},
new Foo{id=4,parentid = 0, name="bar12"},
new Foo{id=5,parentid = 1, name="bar13"},
new Foo{id=6,parentid = 2, name="bar14"},
new Foo{id=7,parentid = 2, name="bar3"}
};
var groups = items.GroupBy(item => item.parentid).ToList();
var firstLevelElements = items.Where(item => item.name == "bar1");
List<Foo> secondLevelElements = new List<Foo>();
foreach (var firstLevelElement in firstLevelElements)
{
secondLevelElements.AddRange(groups[firstLevelElement.id]
.Where(item => item.name == "bar2"));
}
List<Foo> thirdLevelElements = new List<Foo>();
foreach (var secondLevelElement in secondLevelElements)
{
thirdLevelElements.AddRange(groups[secondLevelElement.id]
.Where(item => item.name == "bar3"));
}

C# foreach on a collection of an interface

I'm wondering if there is any functionality built in to C#/LINQ to simplify the following:
foreach(var item in collection)
{
if (item.GetType() == typeof(Type1)
DoType1(item as Type1);
else if (item.GetType() == typeof(Type2))
DoType2(item as Type2);
...
}
to something along the lines of:
collection.ForEachType(Type1 item => DoType1(item), Type2 item => DoType2(item));
I realize that the following is close:
collection.OfType<Type1>.ToList().Foreach(item => DoType1(item));
collection.OfType<Type2>.ToList().Foreach(item => DoType2(item));
But it does not work when the code is dependent on the order of the collection.
The first thing I'd look at is polymorphism; can I instead use a virtual method, and item.DoSomething()?
The next thing I'd look at would be an enum discriminator, i.e.
switch(item.ItemType) {
case ItemType.Foo: ...
case ItemType.Bar: ...
}
(and add the discriminator to the common interface/base-class)
If the types could be anything, then 4.0 has a trick; if you call te method the same thing for every overload, you can get dynamic to worry about picking it:
dynamic x = item;
DoSomething(x);
There's nothing built into LINQ, no. I would caution you against using GetType() like this though - usually it's more appropriate to use is or as followed by a null check:
foreach(var item in collection)
{
Type1 itemType1 = item as Type1;
if (itemType1 != null)
{
DoType1(itemType1);
continue;
}
Type2 itemType2 = item as Type1;
if (itemType2 != null)
{
DoType2(itemType1);
continue;
}
// etc
}
That way derived classes will be treated in a way which is usually the appropriate one.
This sort of type testing is generally frowned upon, mind you - it's generally better to put the behaviour into the type itself as a virtual method, and call it polymorphically.
What about something like:
var typeActions = new Dictionary<Type,Action<Object>>();
typeActions.Add(typeof(Type1), obj => DoType1((Type1)obj));
typeActions.Add(typeof(Type2), obj => DoType2((Type2)obj));
collection.Foreach(obj => typeActions[obj.GetType()](obj));
This code is untested (typed directly into the browser).
Your mileage may vary.
Dictionary<Type, Action<object>> typeMap = new Dictionary<Type, Action<object>>();
typeMap[typeof(Type1)] = item => DoType1(item as Type1);
typeMap[typeof(Type2)] = item => DoType2(item as Type2);
var typeToActionQuery =
from item in source
let type = item.GetType()
where typeMap.ContainsKey(type)
select new
{
input = item;
method = typeMap[type]
};
foreach(var x in typeToActionQuery)
{
x.method(x.input);
}
Here's a version of the matching query which considers derived types (Note, an item may be matched to more than 1 type, and therefore handled multiple times).
var typeToActionQuery =
from item in source
from kvp in typeMap
where kvp.Key.IsInstanceOfType(item)
select new
{
input = item;
method = kvp.Value
};
It seems to me that if you just replace "item.GetType() == typeof( Type1 )" with "item is Type1", your foreach loop will be simple enough.
Not by default. Try Reactive Extensions or Elevate
The Reactive Extensions and Elevate both contain a ForEach implementation. Both have quite a few methods that extend the functionality of linq.
You won't find a ForEachType, but ForEach (Rx or Elevate) and OfType<> (Linq) will give you what you want.

C# List - Group By - Without Linq

I have an object:
IObject
{
string Account,
decimal Amount
}
How do I group by Account and Sum the Amount, returning a List without Linq.
2.0 Framework ... that is why no Linq.
Here is what I have:
ListofObjects = List<IObject>;
foreach (var object in objects)
{
var objectToAdd = new Object(object);
var oa = ListofObjects.Find(x => x.Account == objectToAdd.Account);
if (oa == null)
{
ListofObjects.Add(objectToAdd);
}
else
{
ListofObjects.Remove(oa);
oa.Amount = objectToAdd.Amount;
ListofObjects.Add(oa);
}
}
Easiest answer: use LINQBridge and get all your LINQ to Objects goodness against .NET 2.0... works best if you can use C# 3 (i.e. VS2008 but targeting .NET 2.0).
If you really can't do that, you'll basically need to keep a dictionary from a key to a list of values. Iterate through the sequence, and check whether it already contains a list - if not, add one. Then add to whatever list you've found (whether new or old).
If you need to return the groups in key order, you'll need to also keep a list of keys in the order in which you found them. Frankly it's a pain... just get LINQBridge instead :)
(Seriously, each individual bit of LINQ is actually fairly easy to write - but it's also quite easy to make off-by-one errors, or end up forgetting to optimize something like Count() in the case where it's actually an ICollection<T>... There's no need to reinvent the wheel here.)
EDIT: I was about to write some code, but then I noticed that you want a list returned... a list of what? A List<IList<IObject>>? Or are you actually trying to group and sum in one go? If so, don't you want a list of pairs of key and amount? Or are you going to reuse the same class that you've already got for a single account, but as the aggregate? If it's the latter, here's some sample code:
public static IList<IObject> SumAccounts(IEnumerable<IObject> data)
{
List<IObject> ret = new List<IObject>();
Dictionary<string, IObject> map = new Dictionary<string, IObject>();
foreach (var item in data)
{
IObject existing;
if (!map.TryGetValue(item.Account, out existing))
{
existing = new IObject(item.Account, 0m);
map[item.Account] = existing;
ret.Add(existing);
}
existing.Amount += item.Amount;
}
return ret;
}
Admittedly the extra efficiency here due to using a Dictionary for lookups will be pointless unless you've got really quite a lot of accounts...
EDIT: If you've got a small number of accounts as per your comment, you could use:
public static IList<IObject> SumAccounts(IEnumerable<IObject> data)
{
List<IObject> ret = new List<IObject>();
foreach (var item in data)
{
IObject existing = ret.Find(x => x.Account == item.Account);
if (existing == null)
{
existing = new IObject(item.Account, 0m);
ret.Add(existing);
}
existing.Amount += item.Amount;
}
return ret;
}
Use a dictionary to hold the results. Locating an item in a dictionary is close to an O(1) operation, so it's a lot faster than searching for items in a list.
Dictionary<string, decimal> sum = new Dictionary<string, decimal>();
foreach (IObject obj in objects) {
if (sum.ContainsKey(obj.Account)) {
sum[obj.Account].Amount += obj.Amount;
} else {
sum.Add(obj.Account, obj.Amount);
}
}

Categories