Lambda expression as function parameter - c#

I have the following code
List<int> GetIndices<T>(List<T> list, ?????? condition
{
var result =
list
.Select((p, index) => index)
.Where(condition);
return result.ToList();
}
And I would like to call it like GetIndices(someList, (p, index) => (someList[index].Height < someList[index - 1].Height))
What is the correct type of condition?

There's an error in your code: Where expects a delegate that returns a bool value and has the list element type as input.
var result = list
.Select((p, index) => index) // projects the element to it's index (of type int)
.Where(condition); // => expects Func<int, bool>
So you would need Func<int,bool>
However, from your spec I think you want Func<T,int,bool>, which means you have to rewrite your implementation of GetIndices as
var result = list
.Select((p, index) => new {p, index})
.Where(x => condition(x.p, x.index))
.Select(x => x.index);

Func<T, bool>
Should do the trick but you're going to have to modify your lambda a bit because you can't pass the index (if you want to use condition in the Where clause). You could easily change your lambda to:
p => someList[someList.IndexOf(p).Height < someList[someList.IndexOf(p)-1].Height
For future reference, the MSDN documentation for the extension methods is great once you learn how to read it (that part takes a bit):
MSDN - Enumerable.Where Method
Since this is an extension method, the first parameter (IEnumerable<TSource>) is the collection you're calling the method on (List<T> in your case).
The second parameter is what you need to match. Since the documentation calls for Func<TSource, bool> and TSource is T in your case...you get Func<T, bool>

Like jeroenh realized, you need to capture the original index. The Funct<T,int,bool> condition you pass only needs to be aware of the item and its index, not the anonymous type created in the query, so the condition passed changes a bit. It also should handle the situation where the index == 0 and therefore there are no preceding items (index - 1).
class Program {
static void Main( string[] args ) {
var items = Item.GetItems();
// mind the case where index == 0 so you don't grab an item out of bounds
var ind = GetIndices( items,
( p, index ) => ( h.index == 0 ) ? false : p.Height < items[ index - 1 ].Height );
}
static List<int> GetIndices<T>( List<T> list, Func<T, int, bool> condition ) {
var res = list
.Select( ( item, index ) => new { item, index } ) // capture original index
.Where( h => condition( h.item, h.index ) )
.Select( h => h.index ); // reduce to the index again
return res.ToList();
}
}
class Item {
public int Height {
get;
set;
}
public Item( int h ) {
Height = h;
}
static public List<Item> GetItems() {
return new List<Item>( new[]{
new Item(1),
new Item(4),
new Item(2),
new Item(5)
} );
}
}

Try Func<bool>.
Or rather a variant with the correct amount of input parameters.

Related

Deconstruct Container containing ValueTuple in LINQ

I am able to deconstruct a container via an extension method:
var limits = new[] { MyEnum.A , MyEnum.B , MyEnum.C }
.ToDictionary( x => x ,
x => ( SupportedLimit: GetLimit( x ) , RequiredLimit: 0 ) );
public static class Extensions
{
public static void Deconstruct<TKey, TVal>(
this KeyValuePair<TKey , TVal> tuple ,
out TKey key , out TVal value )
{
key = tuple.Key;
value = tuple.Value;
}
public static void Deconstruct<TKey, TVal1, TVal2>(
this KeyValuePair<TKey , (TVal1,TVal2)> tuple ,
out TKey key , out TVal1 v1 , out TVal2 v2 )
{
key = tuple.Key;
(v1 , v2) = tuple.Value;
}
}
// works
foreach( var ( enumVal , supportedLimit , requiredLimit ) in limits )
Debugger.Break();
How do I deconstruct a container/dictionary containing a System.ValueTuple in LINQ?
// won't work, .Where() expects Func<T,bool> not Func<T1,T2,T3,bool>
var failedLimits = limits.Where( ( _ , sup , req ) => sup < req );
I just wanted to know how (and if) it is possible to deconstruct the ValueTuple in (any) LINQ method. I guess I have to add an extension method for every Linq-method (List-like) + overloads for dictionaries + each amount of values in the ValueTuple.
How would it look like for the Where() in the example?
public static class LinqExtensions
{
public static IEnumerable<KeyValuePair<TKey,(T1,T2)>> Where<TKey,T1,T2>(
this IEnumerable<KeyValuePair<TKey,(T1,T2)>> source ,
Func<TKey,T1,T2, Boolean> predicate )
=> source.Where( predicate );
}
Overloads for List-like types + every amount of ValueTuple-parameter
Since you're dealing with a Dictionary the values you iterate over are KeyValuePairs. You need to deal with the Value part of the KeyValuePair and then just use the named property of your value tuple.
var failedLimits = limits.Where(kvp => kvp.Value.SupportedLimit < req);

Get items from list where index not equal to an int using LINQ

I have items in a Listand I want to get all the items which index is not equal to something,
Say it's a list of button List<Button>. How do I get those buttons with index other than index using something like the following.
var buttons = buttonList.Where(b => b<indexOfbutton> != index);
Update:
I am trying to get <indexOfButton> in one line. So I assume there's another linq query to get the button index from buttonList?
The end goal is to get List which does not contain the index in question.
You can specify the index within lambda expression because there is an another overload of Where method that takes an Func<TSource, int, bool> :
System.Func<TSource, Int32, Boolean>
A function to test each source element for a condition; the second
parameter of the function represents the index of the source element.
var buttons = buttonList.Where((b,idx) => idx != index);
You can also write an extension method for that:
public static class Extensions
{
public static IEnumerable<T> SkipIndex<T>(this IEnumerable<T> source, int index)
{
int counter = 0;
foreach (var item in source)
{
if (counter != index)
yield return item;
counter++;
}
}
}
And use it:
var buttons = buttonList.SkipIndex(index).ToList();
If you want to get buttons with their indices:
var buttons = buttonList
.Select((b,idx) => new { Button = b, Index = idx })
.Where(x => x.Index != index)
.ToList();
This will return you a list of anonymous types that contains two properties, one of them is your button and the other one it's index.
If I have throw-away list (if I do not need the original list and only list without an element) and the buttonList is IList<>, I would use something like
buttonList.RemoveAt(index);

LINQ: Collapsing a series of strings into a set of "ranges"

I have an array of strings similar to this (shown on separate lines to illustrate the pattern):
{ "aa002","aa003","aa004","aa005","aa006","aa007", // note that aa008 is missing
"aa009"
"ba023","ba024","ba025"
"bb025",
"ca002","ca003",
"cb004",
...}
...and the goal is to collapse those strings into this comma-separated string of "ranges":
"aa002-aa007,aa009,ba023-ba025,bb025,ca002-ca003,cb004, ... "
I want to collapse them so I can construct a URL. There are hundreds of elements, but I can still convey all the information if I collapse them this way - putting them all into a URL "longhand" (it has to be a GET, not a POST) isn't feasible.
I've had the idea to separate them into groups using the first two characters as the key - but does anyone have any clever ideas for collapsing those sequences (without gaps) into ranges? I'm struggling with it, and everything I've come up with looks like spaghetti.
So the first thing that you need to do is parse the strings. It's important to have the alphabetic prefix and the integer value separately.
Next you want to group the items on the prefix.
For each of the items in that group, you want to order them by number, and then group items while the previous value's number is one less than the current item's number. (Or, put another way, while the previous item plus one is equal to the current item.)
Once you've grouped all of those items you want to project that group out to a value based on that range's prefix, as well as the first and last number. No other information from these groups is needed.
We then flatten the list of strings for each group into just a regular list of strings, since once we're all done there is no need to separate out ranges from different groups. This is done using SelectMany.
When that's all said and done, that, translated into code, is this:
public static IEnumerable<string> Foo(IEnumerable<string> data)
{
return data.Select(item => new
{
Prefix = item.Substring(0, 2),
Number = int.Parse(item.Substring(2))
})
.GroupBy(item => item.Prefix)
.SelectMany(group => group.OrderBy(item => item.Number)
.GroupWhile((prev, current) =>
prev.Number + 1 == current.Number)
.Select(range =>
RangeAsString(group.Key,
range.First().Number,
range.Last().Number)));
}
The GroupWhile method can be implemented like so:
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
this IEnumerable<T> source, Func<T, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> list = new List<T>() { iterator.Current };
T previous = iterator.Current;
while (iterator.MoveNext())
{
if (!predicate(previous, iterator.Current))
{
yield return list;
list = new List<T>();
}
list.Add(iterator.Current);
previous = iterator.Current;
}
yield return list;
}
}
And then the simple helper method to convert each range into a string:
private static string RangeAsString(string prefix, int start, int end)
{
if (start == end)
return prefix + start;
else
return string.Format("{0}{1}-{0}{2}", prefix, start, end);
}
Here's a LINQ version without the need to add new extension methods:
var data2 = data.Skip(1).Zip(data, (d1, d0) => new
{
value = d1,
jump = d1.Substring(0, 2) == d0.Substring(0, 2)
? int.Parse(d1.Substring(2)) - int.Parse(d0.Substring(2))
: -1,
});
var agg = new { f = data.First(), t = data.First(), };
var query2 =
data2
.Aggregate(new [] { agg }.ToList(), (a, x) =>
{
var last = a.Last();
if (x.jump == 1)
{
a.RemoveAt(a.Count() - 1);
a.Add(new { f = last.f, t = x.value, });
}
else
{
a.Add(new { f = x.value, t = x.value, });
}
return a;
});
var query3 =
from q in query2
select (q.f) + (q.f == q.t ? "" : "-" + q.t);
I get these results:

Codesnippet for sorting collection<T> based on a comma-separated string value

In my code I use a collection<T> as binding source for different controls (WPF/C#). The collection is created from the system every time the application loads. I don't have control on the collection and its order (items within the collection) are randomly for every launch of the app.
For UI reasons I need to allow to sort the collection, display it in a listview and keep the sorting when modified by the user (moveup and movedown buttons). Therefore my Idea was simple. I simply write the items comma-separated into a hidden string variable eg. "itemX,ItemY,ItemZ".
Now I need a function that sorts the collection based on the string. I was thinking of a few foreach loops, but I am sure there is a better way of sorting the collection.
sort the Items within collection<t> in the same order as represented by the string.
string correctItemOrder = "Application5, Application2, Application4, Application3".
Collection<T> has items with just the property name (e.g. "Applicaton3") but is sorted randomly. I want to sort the collection in the same order as the string.
T is an interface an I can access a property "Name" that has the value that is stored in the string eg. "ItemX".
Any cool snippets/functions?
Thanks
A couple of ideas...
Either way, you'll want your "item order string" as an array, so...
var sortOrder = correctItemOrder.Split(new[] { ", " }, StringSplitOptions.None);
Then one option is to order your collection by the order of items in sortOrder (meaning you have to traverse through half of sortOrder, on average, for each element in your collection):
var sortedCollection = new Collection<T>(collection.OrderBy(x => Array.IndexOf(sortOrder, x.Name)).ToList());
Another option is to create a dictionary of Name => item, then traverse sortOrder, selecting items from this dictionary as you go...
var dict = collection.ToDictionary(x => x.Name);
var sortedCollection = new Collection<T>(sortOrder.Select(x => dict[x]).ToList());
It's worth noting that if new items are added to the collection, but not sortOrder, the first snippet will place them at the start of the collection, whereas the second one will discard them entirely. Similarly if items are present in sortOrder but not the collection, the first snippet will ignore them, whereas the second one will throw an exception.
EDIT:
The third option, of course, is to create dictionary from sortOrder, and use that.
var dict = sortOrder.Select((x, i) => new { x, i }).ToDictionary(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => dict[x.Name]).ToList());
EDIT2:
As Enigmativity has pointed out, using lookups instead of dictionaries allows you to handle the cases where dictionary keys are missing very neatly.
The last example using this technique:
var lookup = sortOrder.Select((x, i) => new {x, i}).ToLookup(x => x.x, x => x.i);
var sortedCollection = new Collection<T>(collection.OrderBy(x => lookup[x.Name].DefaultIfEmpty(Int32.MaxValue).First()).ToList());
I think a comparer like this should do the job:
public interface INamed {
string Name {get;}
}
public class CustomComparer : Comparer<INamed> {
Dictionary<string, int> hash;
public CustomComparer( ) {
var tokens = "Application5, Application2, Application4, Application3"
.Split( ',' )
.Select( s => s.Trim( ) )
.ToArray( );
hash = Enumerable.Range(0, tokens.Length)
.ToDictionary( i => tokens[i] );
}
public override int Compare( INamed x, INamed y ) {
return hash[x.Name] - hash[y.Name];
}
public static readonly CustomComparer Default = new CustomComparer();
}
EDIT: I see that Collection has not order by itself, so is needed to build a wrapper
class SortableCollection<T> : System.Collections.ObjectModel.Collection<T>
{
public SortableCollection() : this(new List<T>()) {}
public SortableCollection(List<T> list) : base(list) {}
public virtual void Sort() { ((List<T>)Items).Sort(); }
}
class CustomSortableCollection<T> : SortableCollection<T> where T: INamed
{
public override void Sort() {
((List<INamed>)Items).Sort(CustomComparer.Default);
}
}
This way you can sort the colection when you need it doing:
your_collection.Sort();
You could do this:
var rank =
correctItemOrder
.Split(',')
.Select((x, n) => new { x = x.Trim(), n, })
.ToLookup(z => z.x, z => z.x);
var query =
from i in items
orderby rank[i.Name]
.DefaultIfEmpty(int.MaxValue)
.First()
select i;
This handles missing values in the correctItemOrder string too.

linq way to insert element in order

i have a collection of elements sorted by the elements' Name property. i need to insert a new element into the collection while maintaining the order. i am looking for a concise LINQ way to do this. my code is below. "this.Children" is the collection, and "d" is the new element that i need to insert. it takes two passes over the collection to find the insertion point. is there a way to get the index from the First() extension method? (please do not suggest using foreach, i know that :), i am learning LINQ).
thanks!
konstantin
var v = this.Children.FirstOrDefault(x => string.Compare(x.Name, d.Name) > 0);
int index = this.Children.IndexOf(v);
if (index < 0)
{
this.children.Add(d);
}
else
{
this.Children.Insert(index, d);
}
Yes, using the overload of Select which includes the index as well as the value:
var pair = this.Children
.Select((value, index) => new { value, index })
.FirstOrDefault(x => string.Compare(x.value.Name, d.Name) > 0);
if (pair == null)
{
Children.Add(d);
}
else
{
Children.Insert(pair.index, d);
}
Note that this is still inefficient though - if you already know the values are sorted, you can use a binary chop to find out the insertion index. It's hard to give sample code for that without knowing the type of Children though... there's already List<T>.BinarySearch and Array.BinarySearch.
Learning LINQ is admirable - but it's also important to learn when using LINQ isn't the best way to go :)
Assuming that this.Children is a List<T>, you can use List<T>.BinarySearch with a custom comparer to efficiently find the position to insert the new element at:
IComparer<Foo> comparer = AnonymousComparer.Create<Foo>(
(x, y) => string.Compare(x.Name, y.Name));
int index = this.Children.BinarySearch(d, comparer);
if (index < 0) index = ~index;
this.Children.Insert(index, d);
with
static class AnonymousComparer
{
public static IComparer<T> Create<T>(Func<T, T, int> comparer)
{
if (comparer == null) { throw new ArgumentNullException("comparer"); }
return new TheComparer<T>(comparer);
}
private class TheComparer<T> : IComparer<T>
{
private readonly Func<T, T, int> c;
public TheComparer(Func<T, T, int> c) { this.c = c; }
int IComparer<T>.Compare(T x, T y) { return this.c(x, y); }
}
}
I created my own Extension method for adding a new item in the correct order:
public static class ListExtension
{
public static void InsertOrderedBy<TSource, TKey>(this IList<TSource> source, TSource item, Func<TSource, TKey> keySelector) where TKey : IComparable<TKey>
{
var i = source.Select((Value, Index) => new { Value, Index }).FirstOrDefault(x => keySelector(x.Value).CompareTo(keySelector(item)) > 0);
if (i == null)
{
source.Add(item);
}
else
{
source.Insert(i.Index, item);
}
}
}
I use it like this:
List<Item> ItemList = new List<Item>();
ItemList.InsertOrderedBy(item, x => x.Duration);
It's almost the same like the answer from Jon Skeet, but I can pass the sort argument as second parameter, e.g. a Duration (type TimeSpan).
Well, you could always just use OrderBy after adding the new element...
var v = this.Children.Union(new List<TypeOfChildren>() { d }).OrderBy<TypeOfChildren, string>(x => x.Name).ToList<TypeOfChildren>();

Categories