Linq: Take top elements after group - linq

I have the following query, and I''m trying to get the top 4 of TopicCounts by TCount:
var bygroup = (from element in inputSplit
group element by element.Text into groups
from win in groups.TumblingWindow(TimeSpan.FromSeconds(10))
select new TopicCounts
{
Topic = groups.Key,
TCount = win.Count(),
Score = win.Avg(element => element.Sen)
}
).OrderByDescending(x => x.TCount).Distinct().Take(4);
I get the following error whenever I try to build:
'Microsoft.ComplexEventProcessing.Linq.IQStreamable'
does not contain a definition for 'OrderByDescending' and no extension
method 'OrderByDescending' accepting a first argument of type
'Microsoft.ComplexEventProcessing.Linq.IQStreamable'
could be found (are you missing a using directive or an assembly
reference?)
What am I missing?

IQStreamable cannot be sorted as it is a stream (streams load elements as they are required, a sort requires the entire collection). In order to sort it you need to load the entire collection. This can be done by calling ToList before you sort, however if the collection is large you will end up with high memory usage.

Related

Finding items from a list in an array stored in a DB field

I have a legacy database that has data elements stored as a comma delimited list in a single database field. (I didn't design that, I'm just stuck with it.)
I have a list of strings that I would like to match to any of the individual values in the "array" in the DB field and am not sure how to do this in Linq.
My list:
List<string> items= new List<string>();
items.Add("Item1");
items.Add("Item2");
The DB field "Products" would contain data something like:
"Item1,Item3,Item4"
"Item3,Item5,Item6"
"Item2,Item7,Item6"
"Item1,Item2"
"Item1"
My first pass at the Linq query was:
var results = (from o in Order
.Where(p=> items.Contains(p.Products)
But I know that won't work. because it will only return the records that contain only "Item1" or "Item2". So with the example data above it would return 0 records. I need to have it return two records.
Any suggestions?
There is a simple clever trick for searching comma-separated lists. First, add an extra , to the beginning and end of the target value (the product list), and the search value. Then search for that exact string. So for example, you would search ,Item1,Item3,Item4, for ,Item1,. The purpose of this is to prevent false positives, i.e., Item12,Item3 finding a match for Item1, while allowing items at the beginning/end of the list to be properly found.
Then, you can use the LINQ .Any method to check that any item in your list is a match to the product list, like the following:
var results = (from o in Order
.Where(o => items.Any(i => (","+o.Products+",").Contains(","+i+",")))
One way would be to parse the list in the Products field:
var results = (from o in Order
.Where(o => items.Any(i => o.Products.Split(',').Contains(i))
But that would parse the string multiple times for each record. You could try pulling back ALL of the records, parsing each record once, then doing the comparison:
var results = from o in Order
let prods = o.Products.Split(',')
where items.Any(i => prods.Contains(i))
select o;

Imroving/Modifying LINQ query

I already have a variable containing some groups. I generated that using the following LINQ query:
var historyGroups = from payee in list
group payee by payee.Payee.Name into groups
orderby groups.Key
select new {PayeeName = groups.Key, List = groups };
Now my historyGroups variable can contain many groups. Each of those groups has a key which is a string and Results View is sorted according to that. Now inside each of those groups there is a List corresponding to the key. Inside that List there are elements and each one those element is an object of a particular type. One of it's fields is of type System.DateTime. I want to sort this internal List by date.
Can anyone help with this? May be modify the above query or a new query on variable historyGroups.
Thanks
It is not clear to me what you want to sort on (the payee type definition is missing as well)
var historyGroups = from payee in list
group payee by payee.Payee.Name into groups
orderby groups.Key
select new {
PayeeName = groups.Key,
List = groups.OrderBy(payee2 => payee2.SomeDateTimeField)
};
Is most straightforward.
If you really want to sort only by date (and not time), use SomeDateTimeField.Date.
Inside that List there are elements and each one those element is an object of a particular type. One of it's fields is of type System.DateTime
This leads me to maybe(?) suspect
List = groups.OrderBy(payee2 => payee2.ParticularTypedElement.DateTimeField)
Or perhaps even
List = groups.OrderBy(payee2 => payee2.ObjectsOfParticularType
.OfType<DateTime>()
.FirstOrDefault()
)
I hope next time you can clarfy the question a bit better, so we don't have to guess that much (and come up with a confusing answer)

LINQ to SQL simple query to get single int value

IQueryable<double?> query = (from t in ctx.MyList orderby t.MyNums select t.MyNums).Take(1);
IQueryable<double> q2 = query.Cast<double>();
IEnumerator<double> enumerator = q2.GetEnumerator();
while (enumerator.MoveNext())
{
double d = enumerator.Current;
return System.Convert.ToInt32(d);
}
The context for the above code is that I'm attempting to get the greatest integer value from a SharePoint list column. SharePoint seems to treat all list item values as "Number" so that's the reason that I initially had "double?" and not "int?". How could I write that query better? Also, at the moment, it doesn't work at all.. it says "Can only specify query options (orderby, where, take, skip) after last navigation." What does that mean? Thanks..
Additional Information e.g. "Why Max() doesn't work in SharePoint Web Services"
Go to the following link: http://msdn.microsoft.com/en-us/library/dd673933.aspx. The note contained there says the following:
The set of queries expressible in the LINQ syntax is broader than those enabled in the representational state transfer (REST)-based URI syntax that is used by data services. A NotSupportedException is raised when the query cannot be mapped to a URI in the target data service.
Did you try using Max instead?
double max = ctx.MyList.Max(t => t.MyNums);
return (int) max;
Don't forget to check for a null value in case all items in the list are null
return (int)(ctx.MyList.Max(x => x.MyNums) ?? 0);

Is this a LINQ lazy loading problem?

Something very strange is happening in my program:
I make this query agt.DefaultNr == 1 on a collection and get 3 items as Result:
IEnumerable<Agent> favAgents =
from agt in builtAgents where agt.DefaultNr == 1 select agt;
For every item I set the DefaultNr = 0
foreach (Agent noFavAgt in favAgents)
{
noFavAgt.DefaultNr = 0;
}
I do another query but for some reason my favAgents collection is empty now!
IEnumerable<Agent> smallAgents = (from agt in favAgents
where agt.tempResultCount < 30
orderby agt.tempResultCount descending
select agt);
What is going on here?
Is this a LINQ lazy loading problem?
Looks like there will be some kind of re-query after I set all items = 0 because I my collection is empty!
This is not lazy loading, it's deferred execution. When you define your initial enumerable, you're defining a query, not a collection. You're correct that it's performing a requery; every time you iterate over favAgents, it will execute the query that you defined. If you want to create a list based off of that query that doesn't change, add ToList().
var favAgents =
(from agt in builtAgents where agt.DefaultNr == 1 select agt).ToList();
Doing this will create a list in memory and cache the results of the query at that point in time.
Yes, your favAgents collection will be empty now - you've "turned off" the bit in each element of it that made it match the query! If you iterate over favAgents twice, it will execute the query twice. favAgents represents the query itself, not the results.
If you want to preserve one particular set of results, use ToList or something similar:
favAgents = favAgents.ToList();
That will materialize the query - perform it once and then remember the results in a list, basically. ToArray would have the same effect, but store the results in an array instead.

Checking for duplicates in a complex object using Linq or Lambda expression

I've just started learning linq and lambda expressions, and they seem to be a good fit for finding duplicates in a complex object collection, but I'm getting a little confused and hope someone can help put me back on the path to happy coding.
My object is structured like list.list.uniqueCustomerIdentifier
I need to ensure there are no duplicate uniqueCustomerIdentifier with in the entire complex object. If there are duplicates, I need to identify which are duplicated and return a list of the duplicates.
Unpack the hierarchy
Project each element to its uniqueID property
Group these ID's up
Filter the groups by groups that have more than 1 element
Project each group to the group's key (back to uniqueID)
Enumerate the query and store the result in a list.
var result =
myList
.SelectMany(x => x.InnerList)
.Select(y => y.uniqueCustomerIdentifier)
.GroupBy(id => id)
.Where(g => g.Skip(1).Any())
.Select(g => g.Key)
.ToList()
There is a linq operator Distinct( ), that allows you to filter down to a distinct set of records if you only want the ids. If you have setup your class to override equals you or have an IEqualityComparer you can directly call the Distinct extension method to return the unique results from the list. As an added bonus you can also use the Union and Intersect methods to merge or filter between two lists.
Another option would be to group by the id and then select the first element.
var results = from item in list
group item by item.id into g
select g.First();
If you want to flatten the two list hierarchies, use the SelectMany method to flatten an IEnumerable<IEnumerable<T>> into IEnumerable<T>.

Resources