Select from IEnumerable using LINQ - c#

I have a method that returns an object from cache which I use to populate selectlists within the system.
var r = CacheHelper.GetCacheItem(...);
return new SelectList((IEnumerable)r, "Id", "Name");
r is of type System.Collections.Generic.List<<>f__AnonymousType39<string,int,string>>
By using IEnumerable enumerable = (IEnumerable)r; I see that
enumerable looks like this:
[0]: { Name = "Lost", Id = 1, Area = null }
[1]: { Name = "Found", Id = 2, Area = null }
[2]: { Name = "Stock Adjustment", Id = 3, Area = null }
...
I would like to be able to use LINQ toquery the result set to return a subset, while keeping the full list in memory. Something like:
var s = enumerable.Where(x => x.Area == "myarea");
Answers to similar SO questions suggest using ToList(), but I can't call any LINQ methods on enumerable without getting a System.Collections.IEnumerable' does not contain a definition for 'ToList'.. error message. (I'm using ToList() elsewhere in the code so I have a valid reference to System.Linq.

Ideally, you should keep the type information all the way through the system - and potentially create a named type with the relevant properties. You could then cast back to IEnumerable<SomeConcreteType> later.
If you can't do that though, and assuming you're using C# 4 and .NET 4 or later, you could use dynamic typing:
IEnumerable<dynamic> enumerable = (IEnumerable<dynamic>) r;
...
var s = enumerable.Where(x => x.Area == "myarea");

Related

linq not returning any data

I have two variables. first variable docList of type www.abc.com.docListResponse. It consists of an array of values with docID[0] = "12345", docID[1] = "34567" docID[2] = "32498"
The second variable is of custom type Documents[] docsColl with values docRefID[0] = "12345", docRefID[1] = "34567", DocRefID[2] = "67890"
The ling operator
var docs = docsColl.ToList().Where(i => i.docRefID== docList .FirstOrDefault().docID.ToString()).ToArray();
I'm expecting it to return two records but its returning 0.
If you need to compose a query using another existing collection, you can generally use the Any() method to accomplish this, which will return any records that match your specific constraint.
var docs = docsColl.Where(d => docList.Any(x => d.docID == x.docRefID));
One caveat with this is that if you are using complex types and your initial collection is not in memory (and is in something like a database), LINQ won't know how to properly handle this, so you typically need to either throw a ToList() call prior to your Where() statement :
var docs = docsColl.ToList()
.Where(d => docList.Any(x => d.docID == x.docRefID));
Or you'll need to ensure that the collection you are using to query through (in this case docList) only consists of primitive types like strings :
// Get your IDs to check against
var docRefIds = docList.Select(d => d.docRefID).ToArray();
var docs = docsColl.Where(d => docRefIds.Any(r => d.docID == r));

Return best fit item from collection in C# 3.5 in just a line or two

Here is some sample code I have basically written thousands of times in my life:
// find bestest thingy
Thing bestThing;
float bestGoodness = FLOAT_MIN;
foreach( Thing x in arrayOfThings )
{
float goodness = somefunction( x.property, localvariable );
if( goodness > bestGoodness )
{
bestGoodness = goodness;
bestThing = x;
}
}
return bestThing;
And it seems to me C# should already have something that does this in just a line. Something like:
return arrayOfThings.Max( delegate(x)
{ return somefunction( x.property, localvariable ); });
But that doesn't return the thing (or an index to the thing, which would be fine), that returns the goodness-of-fit value.
So maybe something like:
var sortedByGoodness = from x in arrayOfThings
orderby somefunction( x.property, localvariable ) ascending
select x;
return x.first;
But that's doing a whole sort of the entire array and could be too slow.
Does this exist?
This is what you can do using System.Linq:
var value = arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable))
.First();
If the array can be empty, use .FirstOrDefault(); to avoid exceptions.
You really don't know how this is implemented internally, so you can't assure this will sort the whole array to get the first element. For example, if it was linq to sql, the server would receive a query including the sort and the condition. It wouldn't get the array, then sort it, then get the first element.
In fact, until you don't call First, the first part of the query isn't evaluated. I mean this isn't a two steps evaluation, but a one step evaluation.
var sortedValues =arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable));
// values isn't still evaluated
var value = sortedvalues.First();
// the whole expression is evaluated at this point.
I don't think this is possible in standard LINQ without sorting the enuermable (which is slow in the general case), but you can use the MaxBy() method from the MoreLinq library to achieve this. I always include this library in my projects as it is so useful.
http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/MaxBy.cs
(The code actually looks very similar to what you have, but generalized.)
I would implement IComparable<Thing> and just use arrayOfThings.Max().
Example here:
http://msdn.microsoft.com/en-us/library/bb347632.aspx
I think this is the cleanest approach and IComparable may be of use in other places.
UPDATE
There is also an overloaded Max method that takes a projection function, so you can provide different logic for obtaining height, age, etc.
http://msdn.microsoft.com/en-us/library/bb534962.aspx
I followed the link Porges listed in the comment, How to use LINQ to select object with minimum or maximum property value and ran the following code in LINQPad and verified that both LINQ expressions returned the correct answers.
void Main()
{
var things = new Thing [] {
new Thing { Value = 100 },
new Thing { Value = 22 },
new Thing { Value = 10 },
new Thing { Value = 303 },
new Thing { Value = 223}
};
var query1 = (from t in things
orderby GetGoodness(t) descending
select t).First();
var query2 = things.Aggregate((curMax, x) =>
(curMax == null || (GetGoodness(x) > GetGoodness(curMax)) ? x : curMax));
}
int GetGoodness(Thing thing)
{
return thing.Value * 2;
}
public class Thing
{
public int Value {get; set;}
}
Result from LinqPad

"new" inside concrete type projection is only called once

I've simple Linq2Sql query:
var result = from t in MyContext.MyItems
select new MyViewModelClass()
{
FirstProperty = t,
SecondProperty = new SomeLinq2SqlEntity()
}
The problem is that it seems that new SomeLinq2SqlEntity() is executed only once for the sequence, so all instances of MyViewModelClass in result of the query share the link to one object.
Update: Here is how I quickly check it:
result[0].SecondProperty.MyField = 10;
Using debugger I can check that MyField was set to 10 in all instances.
When I replace LINQ query with foreach, it works as expected:
var result = from t in MyContext.MyItems select t;
var list = new List<MyViewModelClass>();
foreach (var item in result)
{
list.add(new MyViewModelClass()
{
FirstProperty = item,
SecondProperty = new SomeLinq2SqlEntity()
});
}
I haven't found the root of the problem, but the post marked as asnwer provides good workaround. Check this asnwer for the detailed description: "new" inside concrete type projection is only called once
It probably has something to do with weird IQueryable implementation of your provider.
Aducci's answer extracts data from database with AsEnumerable() call and performs the query on that set, which is different from performing it through IQueryable.
For example IQueryable builds the ExpressionTree which it later parses according to the concrete provider (i.e. executing shared code once for optimization), while IEnumerable accepts Func and performs it as you would expect.
You can read more here:
http://msdn.microsoft.com/en-us/vcsharp/ff963710
Have you tried using adding the SomeLinq2SqlEntity object with linq to objects?
var result = (from t in MyContext.MyItems
select new
{
FirstProperty = t
})
.AsEnumerable()
.Select(t => new MyViewModelClass()
{
FirstProperty = t.FirstProperty ,
SecondProperty = new SomeLinq2SqlEntity();
});

Where clause of LINQ statement to find instances of a string within a List<string> collection?

I'm trying to construct a Where clause for a Linq statement which needs to determine whether the AccountNumber values retrieved as below exist in a List<string> collection.
I've thus far tried this:
private void FindAccountNumbers(List<string> AccountNumbers)
{
var query = from abc
select new
{
AccountNumber = abc.AccountNumber
};
query = query.Where(AccountNumbers.Contains(x => x.AccountNumber));
}
However I get the following build error:
The type arguments for method
'System.Linq.Queryable.Where(System.Linq.IQueryable,
System.Linq.Expressions.Expression>)' cannot
be inferred from the usage. Try specifying the type arguments
explicitly.
At runtime, query contains AccountNumber values, and I'm trying to pare this down based on matches found in the AccountNumbers collection (similar to an IN statement in TSQL). Should I be using Intersect instead of Contains? What am I doing wrong??
I think you want to have this:
query = query.Where(x => AccountNumbers.Contains(x.AccountNumber));
This doesn't work?
var query = from x in abc
where AccountNumbers.Contains(x.AccountNumber)
select new { x.AccountNumber };
That would give you back any AccountNumber in that list, unless AccountNumber isn't actually a string. That could be your problem.
Its because your syntax for from is wrong, I'm guessing that your collection is abc of items to match against is abc
The correct syntax would be (Version 1)
var query = from x in abc
select new { AccountNumber = x.AccountNumber };
query = query.Where(x=>AccountNumbers.Contains(x.AccountNumber));
you don't need to do an anonymous type either as you are just wanting the same field you could just do (Version 2)
var query = from x in abc select x.AccountNumber;
query = query.Where(x=>AccountNumbers.Contains(x));
However you could just slap the Where straight onto your original collection. (Version 3)
var query = abc.Where(x=>AccountNumbers.Contains(x.AccountNumber);
Or if you are just trying to find whether any exist in the collection (Version 4)
var query = abc.Any(x=>AccountNumbers.Countains(x.AccountNumber);
Version 1 will return IEnumerable<string>
Version 2 will return IEnumerable<string>
Version 3 will return IEnumerable<type of the items in abc>
Version 4 will return bool
Let me verify what you're trying to do.
You have a collection of objects abc. You want to pull out the AccountNumber from each member of that collection, compare it to the list of account numbers passed in, and determine... what? If there IS any overlap, or WHAT the overlap is?
If the AccountNumber field is a string, you could do this:
private IEnumerable<string> OverlappingAccountNumbers(IEnumerable<string> accountNumbers)
{
return abc.Select(x => x.AccountNumber)
.Intersect(accountNumbers);
}
Or for the boolean case:
private bool AnyOverlappingAccountNumbers(IEnumerable<string> accountNumbers)
{
return abc.Select(x => x.AccountNumber)
.Intersect(accountNumbers)
.Count() > 0;
}
I'd go with this:
private void FindAccountNumbers(List<string> AccountNumbers)
{
// Get a strongly-typed list, instead of an anonymous typed one...
var query = (from a in abc select a.AccountNumber).AsEnumerable();
// Grab a quick intersect
var matched = query.Intersect(AccountNumbers)
}
One liner?
var query = (from a in abc select a.AccountNumber).AsEnumerable().Intersect(AccountNumbers);
The last answer are wrong because did mention one important point and obviously didn't be tested, the first issue is, you can't mix between an sql query than not been execute and a string's list, YOU CAN'T MIX!!! the solution for this problem and tested is:
var AccountNumbers = select accountNumber from table where.... // is a entitie
private void FindAccountNumbers(IQueryable<acounts> AccountNumbers) //entitie object not string
{
var query = from abc
select new
{
AccountNumber = abc.AccountNumber
};
query = query.Join(AccountNumbers, abc => abc.AccountNumber, aco=> aco, (ac, coNum) => cpac);
}
It really works! is necessary to mention this solution is when you are working with linq and entities framework!

C# LINQ select from list

i have a list of event Ids returned from an xml document as shown below
public IEnumerable<EventFeed> GetEventIdsByEventDate(DateTime eventDate)
{
return (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select new EventFeed()
{
EventShowCode = feed.Attribute("Code").Value
}).ToList();
}
i now need to query my database to match events that equal the eventIds returned from the above method. so i would have something like:
select * from eventsdb where eventId in GetEventIdsByEventDate()
how can i do this using LINQ
i cant seem to get any of the answers working.
this is the method that looks up the eventIds from an XML feed
public IList<EventsDetails> GetEventIds(DateTime eventDate)
{
var eventids = (from feed in xmlDoc.Descendants("Show")
from ev in feed.Elements("Event")
where Convert.ToDateTime(ev.Attribute("Date").Value).ToShortDateString() == eventDate.ToShortDateString()
select new EventsDetails()
{
EventId = feed.Attribute("Code").Value
}).ToList();
return eventids;
}
this is the method that looks up the events in my database
public IEnumerable<EventFeed> GetAllEventsFromDatabase()
{
var allEvents = from eventsList in GetEventsList()
select new EventFeed()
{
EventName = eventsList.Title,
EventSummary = eventsList.Introduction,
EventShowCode = eventsList.EventId,
EventImageSmall = eventsList.EventImageThumbUrl,
EventUrl = eventsList.Url,
EventSortBy = eventsList.SortOrder
};
return allEvents.OrderBy(x => x.EventSortBy);
}
and this is the method to look up any matching eventIds in the XML that exist in my database
public IEnumerable<EventFeed> FilteredEvents(DateTime eventDate)
{
return GetAllEventsFromDatabase().Where(p => GetEventIds(eventDate).Contains<EventsDetails>(p.EventShowCode));
}
the project fails to build with the following error:
Error 9 Argument '2': cannot convert from 'string' to 'Events.EventsDetails'
var eventids = GetEventIdsByEventDate(DateTime.Now);
var result = eventsdb.Where(e => eventids.Contains(e));
If you are returnning List<EventFeed> inside the method, you should change the method return type from IEnumerable<EventFeed> to List<EventFeed>.
In likeness of how I found this question using Google, I wanted to take it one step further.
Lets say I have a string[] states and a db Entity of StateCounties and I just want the states from the list returned and not all of the StateCounties.
I would write:
db.StateCounties.Where(x => states.Any(s => x.State.Equals(s))).ToList();
I found this within the sample of CheckBoxList for nu-get.
The "in" in Linq-To-Sql uses a reverse logic compared to a SQL query.
Let's say you have a list of integers, and want to find the items that match those integers.
int[] numbers = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var items = from p in context.Items
where numbers.Contains(p.ItemId)
select p;
Anyway, the above works fine in linq-to-sql but not in EF 1.0. Haven't tried it in EF 4.0
Execute the GetEventIdsByEventDate() method and save the results in a variable, and then you can use the .Contains() method

Categories

Resources