C# call function of object inside lambda and orderby - c#

I'm trying to call a method of an object inside a lambda expression and then order the list. Is it possible to do this in one expression?
this is what i have right now, it works:
// FindItemsResults<Item> is of namespace Microsoft.Exchange.WebServices.Data.FindItemsResults<Item>
FindItemsResults<Item> findResults = service.FindItems(
WellKnownFolderName.Inbox,
view
);
foreach (Item myItem in findResults.Items.Where(o => LoadObject(o) == true).OrderByDescending(o => o.DateTimeCreated))
{
// Do that stuff again...
}
private static bool LoadObject(Item o)
{
o.Load();
return true;
}
What I'm wondering is if there is a way to do this without the call to LoadObject, being able to do the o.Load inside the lambda expression. It's a Void method, so validating it against a Boolean is not going to work.
Thanks for all your input.
Kind regards.

Yes, you can do this.
For example, you can create the extension method:
public static class Extensions
{
public static IEnumerable<Item> Load(this IEnumerable<Item> items)
{
foreach(var item in items)
{
item.Load()
yield return item;
}
}
}
and then call it like:
foreach (var item in findResults.Items.Load().OrderByDescending(o => o.DateTimeCreated))
{
// Do some stuff...
}
But I would say that you can call Load method right inside your loop.

The Where extension method expects a delegate that returns a Boolean value. You cannot supply a VOID. Yacoub Massad was correct in his comment that a LINQ query expression should not have side effects on the underlying data. If you want to remove the foreach loop because you believe it creates cleaner code, then you can use List(of T).ForEach to perform an action on each item, and then on the next line query and order your items. For example:
findResults.Items.ToList().ForEach(o => o.Load(o));
foreach (var item in FindResults.Items.OrderByDescending(o => o.DateTimeCreated))
{
// Do stuff
}

It's not always considered good practice from a pure functional programming point of view, but I define the follow two extension methods:
public static void ForEach<T>(
this IEnumerable<T> pEnumerable,
Action<T> pAction
) {
foreach (var item in pEnumerable)
pAction(item);
}
public static IEnumerable<T> ForEachWithContinue<T>(
this IEnumerable<T> pEnumerable,
Action<T> pAction
) {
pEnumerable.ForEach(pAction);
return pEnumerable;
}
And then you can do the following:
findResults
.Items
.ForEachWithContinue(o => o.Load())
.OrderByDescending(o => o.DateTimeCreated)
.ForEach(o => {
// do more stuff
});

Related

How to retrieve the elements in a List with a lambda expression without for o foreach

I'm try to pass an entity to a method by parameter, but I don't want to do a for or foreach. How can I pass this parameter without using for or foreach with a lambda expression?
This is my code:
private async Task SaveParents()
{
try
{
await Task.Run(() => {
List<Parent> parents = new List<Parent>();
parents.AddRange(GetParents();
parents.AddRange(GetParentsTwo();
_iData.SaveParents(parents); //Here I want to pass each of the elements with lambda
});
}
}
Thank you :D
Out of the box, LINQ doesn't support what you are trying to do.
If you're really adamant about this, though, it's a trivial matter to write an extension method that will do this for you:
public static class LinqExtensions
{
public static void ForEach(this IEnumerable<T> sequence,
Action<T> action)
{
forEach(var item in sequence)
{
action(item);
}
}
}
Then in your code sample (note I had to change the method signature to take a single item):
private async Task SaveParents()
{
try
{
await Task.Run(() => {
List<Parent> parents = new List<Parent>();
parents.AddRange(GetParents();
parents.AddRange(GetParentsTwo();
parents.ForEach(p => _iData.SaveParent);
});
}
}
Note that just because you can do this does not necessarily mean you should. Microsoft's argument against including IEnumerable<T>.ForEach is that queries should not have side-effects. That's a fairly compelling argument.

perform action A for each B in each C ... if D in tree

I'm trying to create an abstract class that performs given tasks on specific elements. Two examples that share logic:
Example A
foreach (DraftDocument draft in drafts)
foreach (Sheet sheet in draft.Sheets)
foreach (Symbol symbol in sheet.Symbols)
if(conditions) do action
Example B
foreach (DraftDocument draft in drafts)
foreach (Sheet sheet in draft.Sheets)
foreach (View view in sheet.Views)
if(conditions) do action
I would like to reuse the foreach logic, and create a function that takes both a condition method and an action method, and performs it on a given dataset (in this case, a list of DraftDocuments).
What would be a good way to create such? I read something about the Func delegate, but I'm not sure how to implement this correctly.
You could do something like
public class DocumentVisitor
{
private readonly IEnumerable<DraftDocument> _drafts;
private void Visit(Action<Sheet> visitor)
{
foreach(DraftDocument draft in _drafts)
foreach(Sheet sheet in draft.Sheets)
visitor(sheet);
}
public DocumentVisitor(IEnumerable<DraftDocument> drafts) => _drafts = drafts;
public void VisitSymbols(Action<Symbol> visitor) =>
Visit(sheet =>
{
foreach(Symbol symbol in sheet.Symbols)
visitor(symbol);
});
public void VisitViews(Action<View> visitor) =>
Visit(sheet=>
{
foreach(View view in sheet.Views)
visitor(view);
});
}
Creating an extension method to encapsulate the predicate and action logic and making use of SelectMany is, IMHO, enough:
public static void DoActionIf<TSource>(
this IEnumerable<TSource> source,
Predicate<TSource> predicate,
Action<TSource> action)
{
foreach (var s in source)
{
if (predicate(s))
action(s);
}
}
And you'd use it like:
drafts.SelectMany(draft => draft.Sheets)
.SelectMany(sheet => sheet.Symbols)
.DoActionIf(symbol => symbol != null,
symbol => Console.WriteLine(sy));

How to convert the following foreach loop to linq code format?

Code:
foreach (var testView in projectDataCandidate.ViewMaps
.Where(vm => !vm.IsNotTestLane)
.Select(this.GenerateTestView))
{
this.SwimLaneViews.Add(testView);
testView.ItemsOrphaned += OnItemsOrphaned;
}
How to write the above foreach loop in linq code format ? Thanks.
projectDataCandidate.ViewMaps
.Where(vm => !vm.IsNotTestLane)
.Select(this.GenerateTestView)
.ToList()
.ForEach(testView =>
{
this.SwimLaneViews.Add(testView);
testView.ItemsOrphaned += OnItemsOrphaned;
});
As others have already pointed out, you're already using LINQ for your query, and LINQ is for queries rather than commands.
Organise your logic
I think you're trying to improve the readability of your code. To do this, I suggest you extract out the query and command into different class members:
private IEnumerable<ViewMap> TestViews
{
get
{
return projectDataCandidate.ViewMaps
.Where(vm => !vm.IsNotTestLane)
.Select(this.GenerateTestView);
}
}
private void Process(ViewMap testView)
{
this.SwimLaneViews.Add(testView);
testView.ItemsOrphaned += OnItemsOrphaned;
}
Simplify your loop
Then your logic becomes the following, which is easy to read:
private void ProcessTestViews()
{
foreach (var testView in TestViews)
Process(testView);
}
Make your loop declarative
If you still don't like the look of the foreach, you could do this, which I think is even easier to read:
private void ProcessTestViews()
{
TestViews.ToList().ForEach(Process);
}
Remove redundant ToList
If you don't want to convert the result to a List since it's not really needed, you could write an extension method to provide ForEach for IEnumerable<T>:
public static class IEnumerableExtensions
{
public static void ForEach<T>(this IEnumerable<T> elements, Action<T> action)
{
foreach (var element in elements)
action(element);
}
}
This allows you to simplify your method a bit further:
private void ProcessTestViews()
{
TestViews.ForEach(Process);
}
try
projectDataCandidate.ViewMaps.Where(vm => !vm.IsNotTestLane)
.Select(this.GenerateTestView).ForEach(xx=>xx.Your logic)
The only way to do this that I can see is to force execution with a ToList, then use the ForEach function.
var objects = projectDataCandidate.ViewMaps
.Where(vm => !vm.IsNotTestLane)
.Select(this.GenerateTestView)
.ToList();
objects.ForEach(a => SwimLaneViews.Add(a));
objects.ForEach(a => a.ItemsOrphaned += OnItemsOrphaned);

Filtering ICollection

This code doesn't work, but:
public virtual ICollection<SomeItem> items { get { return (ICollection<SomeItem>)items.Where(e => e.isVisible == true); } set { ;} }
I'd like to do something to that effect. So to get an ICollection filtered by a property of the collection's elements.
Sure, I could iterate through the elements, and get the right ones, put them in a new collection and return with that, but is there a nicer solution?
Perhaps what you're looking for is an Extension Method?
Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type.
public static class ExtensionMethods
{
public static ICollection<SomeItem> OnlyVisible(this ICollection<SomeItem) items) {
return items.Where(e => e.isVisible).ToList();
}
}
Note that Where returns an IEnumerable, which you cannot modify, so I call ToList() which essentially does everything in your last sentence.
You would then use it like this:
void Foo(ICollection<SomeItem> items) {
foreach (var i in items.OnlyVisible()) {
// Use i
}
}
Try:
items.Where(e => e.isVisible == true).ToList()

Method in ICollection in C# that adds all elements of another ICollection to it

Is there some method in ICollection in C# that would add all elements of another collection?
Right now I have to always write foreach cycle for this:
ICollection<Letter> allLetters = ... //some initalization
ICollection<Letter> justWrittenLetters = ... //some initalization
... //some code, adding to elements to those ICollections
foreach(Letter newLetter in justWrittenLetters){
allLetters.add(newLetter);
}
My question is, is there method that can replace that cycle? Like for example the method addAll(Collection c) in Java ? So I would write only something like:
allLetters.addAll(justWrittenLetters);
There isn't a method like this for ICollection. You have two options, either use a different type such as List which has the AddRange() method or alternatively, create an extension method:
public static class CollectionExtensions
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> newItems)
{
foreach (T item in newItems)
{
collection.Add(item);
}
}
}

Categories

Resources