How do I implement a matching algorithm using predicates? - c#

I understand how to use delegates and I am okay with lambda expressions to make use of predicates. I've come to a point where I want to implement a method that uses a predicate as an argument and can't figure out how to reference the predicate to find the matches in my collection:
private static T FindInCollection<T>(ICollection<T> collection, Predicate<T> match)
{
foreach (T item in collection)
{
//So how do I reference match to return the matching item?
}
return default(T);
}
I want to then reference this using something akin to:
ICollection<MyTestClass> receivedList = //Some list I've received from somewhere else
MyTestClass UsefulItem = FindInCollection<MyTestClass>(receivedList, i => i.SomeField = "TheMatchingData");
If anyone can give me an explanation or point me to a reference regarding implementation of predicates, I'd appreciate it. The documentation out there seems to all relate to passing predicates (which I can do just fine), not actually implementing the functionality that uses them...
Thanks

private static T FindInCollection<T>(ICollection<T> collection, Predicate<T> match)
{
foreach (T item in collection)
{
if (match(item))
return item;
}
return default(T);
}
You just use the predicate like any other delegate. It's basically a method you can call with any argument of type T, which will return true.

Related

Trying to append objects to an Enumerable in a foreach loop

I'm doing a C# exercise to create an operation that takes a collection, performs a function on each object in the collection, and returns a collection of modified objects.
My code is currently as follows:
public static IEnumerable<U> Accumulate<T, U>(this IEnumerable<T> collection, Func<T, U> func)
{
IEnumerable<U> output = Enumerable.Empty<U>();
foreach (T item in collection)
{
output.Append(func(item));
}
return output;
}
This is only returning an empty collection, and I have no idea why.
I have tried creating a copy of the item in the foreach after seeing this approach in another thread, like so:
foreach (T item in collection)
{
U copy = func(item);
output.Append(copy);
}
but that didn't solve anything.
I did some research but couldn't really find any examples doing exactly what I'm trying to do here. I read some things about closure, but couldn't really understand it, as I'm new to C#.
To answer your actual question: The reason it isn't working is because
output.Append(func(item));
doesn't change output - instead, it returns a new sequence which is func(item) appended to output. Thus when you eventually return output you are just returning the original, empty sequence.
You could make yours work by this simple change:
output = output.Append(func(item));
However, this is not an efficient approach - you're much better off using yield, by modifying your method as follows:
public static IEnumerable<U> Accumulate<T, U>(this IEnumerable<T> collection, Func<T, U> func)
{
foreach (T item in collection)
{
yield return func(item);
}
}
Although note that that is more simply expressed as:
public static IEnumerable<U> Accumulate<T, U>(this IEnumerable<T> collection, Func<T, U> func)
{
return collection.Select(item => func(item));
}
But it is useful to know about how to do this with yield so that you can write solutions to more complex Linq-like problems.
Usually, when I want to achieve this kind of behaviour, I make use of C# Iterators.
They are so usefull when you want to process an iteration on some kind of data and, at each iteration, return a value that is appended to your resulting collection.
Take a look at the docs: MS Docs

Checking if all items in one generic collection exist in another using custom comparison delegate

I have a situation where I need a generic method to which I can pass two collections of type T along with a delegate that compares the two collections and returns true if every element in collection 1 has an equal element in collection 2, even if they are not in the same index of the collection. What I mean by "equal" is handled by the delegate. My initial thought was to return false if the collections were different lengths and otherwise sort them and then compare them like parallel arrays. Then it occurred to me that I can't sort a collection of a generic type without the types sharing an interface. So now I am thinking a LINQ expression might do the trick, but I can't think of how to write it. Consider my current code:
private static bool HasSameCollectionItems<T>(ICollection<T> left, ICollection<T> right, Func<T, T, bool> func)
{
if (left.Count != right.Count)
{
return false;
}
foreach (var item in left)
{
bool leftItemIsInRightCollection = ??? MAGIC ???
if (!leftItemIsInRightCollection)
{
return false;
}
}
return true;
}
I would like to replace ??? MAGIC ??? with a LINQ expression to see if item is "equal" to an element in right using the passed in delegate func. Is this even possible?
Note: For reasons I don't want to bother getting into here, impelemnting IEquatable or overriding the Equals method is not an option here.
It looks like you want .All() and .Any() methods (first method checks that all elements satisfy condition second only check if such an element exist) :
bool leftItemIsInRightCollection = right.Any(rItem => func(item, rItem));
Also i'd refactor your code to something like :
private static bool HasSameCollectionItems<T>(ICollection<T> left, ICollection<T> right, Func<T, T, bool> func)
{
return left.Count == right.Count && left.All(LI => right.Any(RI => func(LI, RI)));
}
The following works by checking whether there are element in left which are not in right.
If you insist on a delegate to determine equality, you can use the FuncEqualityComparer from here. (Note that you must also provide an implementation for Object.GetHashCode)
private static bool HasSameCollectionItems<T>(ICollection<T> left, ICollection<T> right, IEqualityComparer<T> comparer)
{
if (left.Count != right.Count) return false;
return !left.Except(right, comparer).Any();
}

Is this achievable with a single LINQ query?

Suppose I have a given object of type IEnumerable<string> which is the return value of method SomeMethod(), and which contains no repeated elements. I would like to be able to "zip" the following lines in a single LINQ query:
IEnumerable<string> someList = SomeMethod();
if (someList.Contains(givenString))
{
return (someList.Where(givenString));
}
else
{
return (someList);
}
Edit: I mistakenly used Single instead of First. Corrected now.
I know I can "zip" this by using the ternary operator, but that's just not the point. I would just list to be able to achieve this with a single line. Is that possible?
This will return items with given string or all items if given is not present in the list:
someList.Where(i => i == givenString || !someList.Contains(givenString))
The nature of your desired output requires that you either make two requests for the data, like you are now, or buffer the non-matches to return if no matches are found. The later would be especially useful in cases where actually getting the data is a relatively expensive call (eg: database query or WCF service). The buffering method would look like this:
static IEnumerable<T> AllIfNone<T>(this IEnumerable<T> source,
Func<T, bool> predicate)
{
//argument checking ignored for sample purposes
var buffer = new List<T>();
bool foundFirst = false;
foreach (var item in source)
{
if (predicate(item))
{
foundFirst = true;
yield return item;
}
else if (!foundFirst)
{
buffer.Add(item);
}
}
if (!foundFirst)
{
foreach (var item in buffer)
{
yield return item;
}
}
}
The laziness of this method is either that of Where or ToList depending on if the collection contains a match or not. If it does, you should get execution similar to Where. If not, you will get roughly the execution of calling ToList (with the overhead of all the failed filter checks) and iterating the result.
What is wrong with the ternary operator?
someList.Any(s => s == givenString) ? someList.Where(s => s == givenString) : someList;
It would be better to do the Where followed by the Any but I can't think of how to one-line that.
var reducedEnumerable = someList.Where(s => s == givenString);
return reducedEnumerable.Any() ? reducedEnumerable : someList;
It is not possible to change the return type on the method, which is what you're asking. The first condition returns a string and the second condition returns a collection of strings.
Just return the IEnumerable<string> collection, and call Single on the return value like this:
string test = ReturnCollectionOfStrings().Single(x => x == "test");

Is there a neater linq way to 'Union' a single item?

If I have two sequences and I want to process them both together, I can union them and away we go.
Now lets say I have a single item I want to process between the two sequencs. I can get it in by creating an array with a single item, but is there a neater way? i.e.
var top = new string[] { "Crusty bread", "Mayonnaise" };
string filling = "BTL";
var bottom = new string[] { "Mayonnaise", "Crusty bread" };
// Will not compile, filling is a string, therefore is not Enumerable
//var sandwich = top.Union(filling).Union(bottom);
// Compiles and works, but feels grungy (looks like it might be smelly)
var sandwich = top.Union(new string[]{filling}).Union(bottom);
foreach (var item in sandwich)
Process(item);
Is there an approved way of doing this, or is this the approved way?
Thanks
One option is to overload it yourself:
public static IEnumerable<T> Union<T>(this IEnumerable<T> source, T item)
{
return source.Union(Enumerable.Repeat(item, 1));
}
That's what we did with Concat in MoreLINQ.
The new way of doing this, supported in .NET Core and .NET Framework from version 4.7.1, is using the Append extension method.
This will make your code as easy and elegant as
var sandwich = top.Append(filling).Union(bottom);
Consider using even more flexible approach:
public static IEnumerable<T> Union<T>(this IEnumerable<T> source, params T[] items)
{
return source.Union((IEnumerable<T>)items);
}
Works for single as well as multiple items.
You may also accept null source values:
public static IEnumerable<T> Union<T>(this IEnumerable<T> source, params T[] items)
{
return source != null ? source.Union((IEnumerable<T>)items) : items;
}
I tend to have the following somewhere in my code:
public static IEnumerable<T> EmitFromEnum<T>(this T item)
{
yield return item;
}
While it's not as neat to call col.Union(obj.EmitFromEnum()); as col.Union(obj) it does mean that this single extension method covers all other cases I might want such a single-item enumeration.
Update: With .NET Core you can now use .Append() or .Prepend() to add a single element to an enumerable. The implementation is optimised to avoid generating too many IEnumerator implementations behind the scenes.

How does the Search Predicate of Findall work in a generic List

I am able to work with the findAll and it's predicate...but I am wondering about the mechanics that makes it work.
List<BradCollection> b = myList.FindAll(SearchForMe);
List<BradCollection> SearchForMe(string str)
{
if (str.Contains("brad"))
return true;
else
return false;
}
where is the "str" argument in the SearchForMe obtained?
I'm not exactly sure what you're asking; perhaps a look at a simplified implementation of FindAll would help. This isn't how FindAll is actually implemented but it is close enough to understand what's going on:
public delegate bool Predicate<A>(A arg);
...
public List<T> FindAll(Predicate<T> predicate)
{
var result = new List<T>();
foreach (T item in this)
if (predicate(item))
result.Add(item);
return result;
}
Does that answer your question?
It is obtained from myList which likely implements IList<string>, which inherits from IEnumerable<string>. It enumerates each item in myList and then the generic parameter is passed to do the predicate.
If you look, there is the <T> generic parameter on the Predicate.
http://msdn.microsoft.com/en-us/library/bfcke1bz.aspx

Categories