Deconstruct Container containing ValueTuple in LINQ - c#

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);

Related

Elements aren't added to a list that's passed in to a recursive function

I have a function that takes in a transform, string name, and list. It's supposed to do a recursive search through the transform to find all children whose name matches the passed in string and add it to the passed in list.
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
private void Start()
{
FindAllChildren(transform, "Blood", out var bloodParticles);
print(bloodParticles.Count);
}
private void FindAllChildren(Transform parent, string name, out List<Transform> list)
{
list = new List<Transform>();
foreach (Transform child in parent)
{
if (child.name == name)
{
list.Add(child);
}
FindAllChildren(child, name, out list);
}
}
}
The console prints that the list count is zero, when it should be 3. If the list is declared and initialized outside of the function and I omit the 'out' keyword, it prints 3. Why is this?
You're creating a new List<> in each invocation of FindAllChildren which overwrites the last value.
Create the List<> only once in the root entrypoint.
And remove the out modifier:
Like so:
private void Start()
{
List<Transform> bloodParticles = new List<Transform>();
FindAllChildren( transform, "Blood", bloodParticles );
print( bloodParticles.Count );
}
private void FindAllChildren( Transform parent, string name, List<Transform> list )
{
foreach( Transform child in parent )
{
if( child.name == name )
{
list.Add( child );
}
FindAllChildren( child, name, list );
}
}
Personally I'd use an iterator rather than appending to a List<>:
Note that the term "Child" should only refer to the immediate children of a node. If you're traversing multiple levels of a tree structure then the term is "descendant".
private static IEnumerable<Transform> GetDescendants( this Transform node, Func<Transform,Boolean> predicate )
{
if( predicate( node ) ) yield return node;
foreach( Transform child in node )
{
foreach( Transform more in GetDescendants( child, predicate ) )
{
yield return more;
}
}
}
This way you can use it like this:
private void Start()
{
List<Transform> bloodParticles = transform
.GetDescendants( t => t.name == "Blood" )
.ToList();
print( bloodParticles.Count );
}
Update: Regarding Predicate<T> instead of Func<T,Boolean>:
The Predicate<T> delegate predates Func<T,Boolean> and its use should be discouraged. It was added in .NET 2.0 in 2005.
All the modern frameworks like Linq, Entity Framework, etc all use Func<T,Boolean> instead. The Func<T>/Action<T>-family were added to .NET in the .NET Framework 3.5 in 2007.
The older Predicate<T> type is only used by a small number of types and methods in .NET, specifically:
System.Array
System.Array.Find, FindAll, FindIndex, etc
System.Collections.Generic.List<T>.Exists
System.Collections.Generic.List<T>.Find, FindAll, FindIndex, etc
That's it.
If you do want to perform those operations on a List<T> or Array then you can use the Linq extension methods (which use Func<T,Boolean> instead of Predicate<T>) and they'll perform with the same performance as the original methods, albeit with the possible exception of expressions using Skip - and the backwards-iterating methods FindLast, FindLastIndex, etc which can be reimplemented easily enough but are beyond the scope of this answer - and List<T>.RemoveAll which is beyond the scope of Linq as Linq is only concerned with read-only queries, not mutating data.
Classic Predicate<T> methods Linq Func<T,Boolean> alternative
------------------------------------------------------------------------
List<T>.Exists( f ) .Any( f )
List<T>.Find( f ) .FirstOrDefault( f )
List<T>.FindAll( f ) .Where( f ).ToList()
List<T>.FindIndex( f ) .Select( ( e, idx ) => ( e, idx ) ).FirstOrDefault( f )?.idx
List<T>.FindIndex( startIndex, f ) .Skip( startIndex ).Select( ( e, idx ) => ( e, idx ) ).FirstOrDefault( f )?.idx
List<T>.FindIndex( startIndex, count, f ) .Skip( startIndex ).Take( count ).Select( ( e, idx ) => ( e, idx ) ).FirstOrDefault( f )?.idx
List<T>.TrueForAll( f ) .All( f )
While C# does not allow for structural equivalence of nominative delegate types (which is understandable given C# and .NET is couched in a nominative type system with hardly any structural type support), C# does allow for implicit conversion from a method-reference (be it a class member method, delegate() {}-style anonymous method, () => -style lambda method, or inline function) to any compatible delegate type. Though this won't help you much here.
...so if you really need compatibility with a prior Predicate<T> reference, just add an extension method to invoke it:
private static IEnumerable<Transform> GetDescendants( this Transform node, Predicate<Transform> legacyPredicate )
{
if( legacyPredicate is null ) throw new ArgumentNullException(nameof(legacyPredicate));
return GetDescendants( node: node, predicate: t => legacyPredicate( t ) );
}

How to Except<> specifing another key? Or faster way to differences two huge List<>?

I have a list of AE_AlignedPartners items in the db, which I retrieve with:
List<AE_AlignedPartners> ae_alignedPartners_olds = ctx.AE_AlignedPartners.AsNoTracking().ToList();
Than, I got and serialize a new list (of the same object type) with JSON:
List<AE_AlignedPartners> ae_alignedPartners_news = GetJSONPartnersList();
Than I'm getting the intersections of both:
var IDSIntersections = (from itemNew in ae_alignedPartners_news
join itemOld in ae_alignedPartners_olds on itemNew.ObjectID equals itemOld.ObjectID
select itemNew).Select(p => p.ObjectID).ToList();
Now, due of these intersections, I need to create two new lists, with the added items (ae_alignedPartners_news - intersections) and the deleted ones (ae_alignedPartners_olds - interesections). Here's the code:
// to create
IList<AE_AlignedPartners> ae_alignedPartners_toCreate = ae_alignedPartners_news.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();
// to delete
IList<AE_AlignedPartners> ae_alignedPartners_toDelete = ae_alignedPartners_olds.Where(p => !IDSIntersections.Contains(p.ObjectID)).ToList();
But with many records (~100k) it tooks too much time.
Is there a sort of Except<> specifing which key need to be compared? In my case its not p.ID (which is the Primary Key on the DB), but p.ObjectID.
Or any other faster way?
There is an Except function that you can use with a custom comparer:
class PartnerComparer : IEqualityComparer<AE_AlignedPartners>
{
// Partners are equal if their ObjectID's are equal.
public bool Equals(AE_AlignedPartners x, AE_AlignedPartners y)
{
//Check whether the partner's ObjectID's are equal.
return x.ObjectID == y.ObjectID;
}
public int GetHashCode(AE_AlignedPartners ap) {
return ap.ObjectId.GetHashCode();
}
}
var intersect = ae_alignedPartners_news.Intersect(ae_alignedPartners_olds);
var creates = ae_alignedPartners_news.Except(intersect, new PartnerComparer);
var deletes = ae_alignedPartners_old.Except(intersect, new PartnerComparer);
This should give you a reasonable boost in performance.
You don't need an inner join, you need a full outer join on primary key. LINQ does not know a full outer join, but it is easy to extend IEnumerable with a function.
from StackOverlow: LINQ full outer join, I took the solution that uses deferred execution. This solution only works if the KeySelector uses unique keys.
public static IEnumerable<TResult> FullOuterJoin<TA, TB, TKey, TResult>(
this IEnumerable<TA> sequenceA,
IEnumerable<TB> sequenceB,
Func<TA, TKey> keyASelector,
Func<TB, TKey> keyBSelector,
Func<TKey, TA, TB, TResult> resultSelector,
IEqualityComparer<TKey> comparer)
{
if (comparer == null) comparer = EqualityComparer<TKey>.Default;
// create two lookup tables:
var alookup = a.ToLookup(selectKeyA, comparer);
var blookup = b.ToLookup(selectKeyB, comparer);
// all used keys:
var aKeys = alookup.Select(p => p.Key);
var bKeys = blookup.Select(p => p.Key);
var allUsedKeys = aKeys.bKeys.Distinct(comparer);
// for every used key:
// get the values from A with this key, or default if it is not a key used by A
// and the value from B with this key, or default if it is not a key used by B
// put the key, and the fetched values in the ResultSelector
foreach (TKey key in allUsedKeys)
{
TA fetchedA = aLookup[key].FirstOrDefault();
TB fetchedB = bLookup[key].FirstOrDefault();
TResult result = ResultSelector(key, fetchedA, fetchedB);
yield result;
}
I use this function to create three types:
Values in A but not in B: (A, null) => must be added
Values in B but not in A: (null, B) => must be removed
Values in A and in B: (A, B) => need further inspection to see if update is needed
.
IEnumerable<AlignedPartners> olds = ...
IEnumerable<AlignedPartners> news = ...
var joinResult = olds.FullOuterJoin(news, // join old and new
oldItem => oldItem.Id, // from every old take the Id
newItem => newItem.Id, // from every new take the Id
(key, oldItem, newItem) => new // when they match make one new object
{ // containing the following properties
OldItem = oldItem,
NewItem = newItem,
});
Note: until now nothing has been enumerated!
foreach (var joinedItem in joinResult)
{
if (joinedItem.OldItem == null)
{
// we won't have both items null, so we know NewItem is not null
AddItem(joinedItem.NewItem);
}
else if (joinedItem.NewItem == null)
{ // old not null, new equals null
DeleteItem(joinedItem.OldItem);
}
else
{ // both old and new not null, if desired: check if update needed
if (!comparer.Equals(old, new))
{ // changed
UpdateItems(old, new)
}
}
}

Linq-SQL Generic GetById with a custom Expression

I've created a generic to target tables in my database. I've also added a generic .where() with an expression containing only 1 parameter in the lambda expression. Is there a way that I can add more than 1 parameter in the same expression? Please ignore the fact that the below method returns 1 item, I would like to make it return a IList.
Here's my method:
public virtual T GetById( Int32 id, String someStringColumn )
{
ParameterExpression itemParameter = Expression.Parameter( typeof( T ), "item" );
var whereExpression = Expression.Lambda<Func<T, Boolean>>
(
Expression.Equal( Expression.Property( itemParameter, "Id" ), Expression.Constant( id ) ),
new[] { itemParameter }
);
return context.Set<T>().Where( whereExpression ).FirstOrDefault();
}
My actual intentions for this method is to later perform a Contains() on "string" properties of the target table "T". I would like to do something like below and append to the above Expression a check if the String property contains a value in the "someStringColumn". Just an insight, the "someStringColumn" will be a general search string on my page past by Ajax on every back-end call.
var properties = item.GetType().GetProperties().Where( p => p.PropertyType == typeof( string ) ).ToArray();
for ( Int32 i = 0; i < properties.Length; i++ )
{
}
I'm trying to achieve something like this in a non-generic method:
public override List<TableInDatabase> List( PagingModel pm, CustomSearchModel csm )
{
String[] qs = ( pm.Query ?? "" ).Split( ' ' );
return context.TableInDatabase
.Where( t => ( qs.Any( q => q != "" ) ? qs.Contains( t.ColumnName) : true ) )
.OrderBy( String.Format( "{0} {1}", pm.SortBy, pm.Sort ) )
.Skip( pm.Skip )
.Take( pm.Take )
.ToList();
}
If I understand correctly, you are seeking for something like this:
var item = Expression.Parameter(typeof(T), "item");
Expression body = Expression.Equal(Expression.Property(item, "Id"), Expression.Constant(id));
if (!string.IsNullOrEmpty(someStringColumn))
{
var properties = typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)).ToList();
if (properties.Any())
body = Expression.AndAlso(body,
properties.Select(p => (Expression)Expression.Call(
Expression.Property(item, p), "Contains", Type.EmptyTypes, Expression.Constant(someStringColumn))
).Aggregate(Expression.OrElse));
}
var whereExpression = Expression.Lambda<Func<T, bool>>(body, item);
i.e. build Contains expression for each string property, combine them using Or and finally combine the result with the first condition using And.

How to create an Expression AND clause from two expressions

I am trying to create a where clause for my view using LINQ.
I was able to create single column where clause and I would like now to create multiple column where clauses..
I have seen code to implement in .Net 4 and above, but since I have to use .Net 3.5, I need a quick work around for this. so what I am trying to do is....
Expression leftexp = {tag=>((tag.id=2)||(tag.id=3))}
Expression rightexp = {tag=>((tag.uid="MU")||(tag.uid="ST"))}
from these two expressions i would like to create
BinaryExpression be = {tag=>((tag.id=2)||(tag.id=3))} &&
{tag=>((tag.uid="MU")||(tag.uid="ST"))}
something like this which i could pass to my where clause in LINQ.
I tried to use Expression.And(leftexp,rightexp)
but got the error..
The binary operator And is not defined for the types
'System.Func2[WebApplication1.View_MyView,System.Boolean]' and
'System.Func2[WebApplication1.View_MyView,System.Boolean]'.
Expression is new for me and might have looked at too much of code so a bit confused to how to go about doing this... would really appreciate if you could point me in the right direction.
Rewriting expressions has been made easy with the addition of ExpressionVisitor to BCL. With some helpers the task gets almost trivial.
Here's a visitor class I use to apply a delegate to the tree nodes:
internal sealed class ExpressionDelegateVisitor : ExpressionVisitor {
private readonly Func<Expression , Expression> m_Visitor;
private readonly bool m_Recursive;
public static Expression Visit ( Expression exp , Func<Expression , Expression> visitor , bool recursive ) {
return new ExpressionDelegateVisitor ( visitor , recursive ).Visit ( exp );
}
private ExpressionDelegateVisitor ( Func<Expression , Expression> visitor , bool recursive ) {
if ( visitor == null ) throw new ArgumentNullException ( nameof(visitor) );
m_Visitor = visitor;
m_Recursive = recursive;
}
public override Expression Visit ( Expression node ) {
if ( m_Recursive ) {
return base.Visit ( m_Visitor ( node ) );
}
else {
var visited = m_Visitor ( node );
if ( visited == node ) return base.Visit ( visited );
return visited;
}
}
}
And here are the helper methods to simplify the rewriting:
public static class SystemLinqExpressionsExpressionExtensions {
public static Expression Visit ( this Expression self , Func<Expression , Expression> visitor , bool recursive = false ) {
return ExpressionDelegateVisitor.Visit ( self , visitor , recursive );
}
public static Expression Replace ( this Expression self , Expression source , Expression target ) {
return self.Visit ( x => x == source ? target : x );
}
public static Expression<Func<T , bool>> CombineAnd<T> ( this Expression<Func<T , bool>> self , Expression<Func<T , bool>> other ) {
var parameter = Expression.Parameter ( typeof ( T ) , "a" );
return Expression.Lambda<Func<T , bool>> (
Expression.AndAlso (
self.Body.Replace ( self.Parameters[0] , parameter ) ,
other.Body.Replace ( other.Parameters[0] , parameter )
) ,
parameter
);
}
}
Which allows to combine the expressions like this:
static void Main () {
Expression<Func<int , bool>> leftExp = a => a > 3;
Expression<Func<int , bool>> rightExp = a => a < 7;
var andExp = leftExp.CombineAnd ( rightExp );
}
UPDATE:
In case ExpressionVisitor's not available, its source had been published a while ago here. Our library used that implementation until we've migrated to .NET 4.
You cannot do that without rewriting both complete expression trees into a complete new one.
Reason: the parameter-expression objects must be the same for the whole expression tree. If you combine the two, you have two parameter-expression objects for the same parameter, which will not work.
It shows with the following code:
Expression<Func<Tab, bool>> leftexp = tag => ((tag.id == 2) || (tag.id == 3));
Expression<Func<Tab, bool>> rightexp = tag => ((tag.uid == "MU") || (tag.uid == "ST"));
Expression binaryexp = Expression.AndAlso(leftexp.Body, rightexp.Body);
ParameterExpression[] parameters = new ParameterExpression[1] {
Expression.Parameter(typeof(Tab), leftexp.Parameters.First().Name)
};
Expression<Func<Tab, bool>> lambdaExp = Expression.Lambda<Func<Tab, bool>>(binaryexp, parameters);
var lambda = lambdaExp.Compile();
This fails on the lambdaExp.Compile() call, which gives the following exception:
Lambda Parameter not in scope
This is caused by the fact that basically I'm re-using the leftexp and rightexp expression, but they have different parameter-expressions, both which are not given by me to the Expression.Lambda<Func<Tab>>(...) call. Deep down into the leftexp and rightexp there are parameter-expression objects which must match the one given to the Expression.Lambda<Func<Tab>>(...) call.
To solve this you have recreate the complete expression using a new (single) parameter-expression for parameter tag.
See here for more information about the problem.

Lambda expression as function parameter

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.

Categories