Count occurrences of values across multiple columns - c#

I am having a terrible time finding a solution to what I am sure is a simple problem.
I started an app with data in Lists of objects. It's pertinent objects used to look like this (very simplified):
class A {
int[] Nums;
}
and
List<A> myListOfA;
I wanted to count occurrences of values in the member array over all the List.
I found this solution somehow:
var results
from a in myListOfA
from n in a.Nums
group n by n into g
orderby g.Key
select new{ number = g.Key, Occurences = g.Count}
int NumberOfValues = results.Count();
That worked well and I was able to generate the histogram I wanted from the query.
Now I have converted to using an SQL database. The table I am using now looks like this:
MyTable {
int Value1;
int Value2;
int Value3;
int Value4;
int Value5;
int Value6;
}
I have a DataContext that maps to the DB.
I cannot figure out how to translate the previous LINQ statement to work with this. I have tried this:
MyDataContext myContext;
var results =
from d in myContext.MyTable
from n in new{ d.Value1, d.Value2, d.Value3, d.Value4, d.Value5, d.Value6 }
group n by n into g
orderby g.Key
select new { number = g.Key, Occurences = g.Count() };
I have tried some variations on the constructed array like adding .AsQueryable() at the end - something I saw somewhere else. I have tried using group to create the array of values but nothing works. I am a relative newbie when it come to database languages. I just cannot find any clue anywhere on the web. Maybe I am not asking the right question. Any help is appreciated.

I received help on a microsoft site. The problem is mixing LINQ to SQL with LINQ to Objects.
This is how the query should be stated:
var results =
from d in MyContext.MyTable.AsEnumerable()
from n in new[]{d.Value1, d.Value2, d.Value3, d.Value4, d.Value5, d.Value6}
group n by n into g
orderby g.Key
select new {number = g.Key, Occureneces = g.Count()};
Works like a charm.

If you wish to use LINQ to SQL, you could try this "hack" that I recently discovered. It isn't the prettiest most cleanest code, but at least you won't have to revert to using LINQ to Objects.
var query =
from d in MyContext.MyTable
let v1 = MyContext.MyTable.Where(dd => dd.ID == d.ID).Select(dd => dd.Value1)
let v2 = MyContext.MyTable.Where(dd => dd.ID == d.ID).Select(dd => dd.Value2)
// ...
let v6 = MyContext.MyTable.Where(dd => dd.ID == d.ID).Select(dd => dd.Value6)
from n in v1.Concat(v2).Concat(v3).Concat(v4).Concat(v5).Concat(v6)
group 1 by n into g
orderby g.Key
select new
{
number = g.Key,
Occureneces = g.Count(),
};

How about creating your int array on the fly?
var results =
from d in myContext.MyTable
from n in new int[] { d.Value1, d.Value2, d.Value3, d.Value4, d.Value5, d.Value6 }
group n by n into g
orderby g.Key
select new { number = g.Key, Occurences = g.Count() };

In a relational database, such as SQL Server, collections are represented as tables. So you should actually have two tables - Samples and Values. The Keys table would represent a single "A" object, while the Values table would represent each element in A.Nums, with a foreign key pointing to the one of the records in the Samples table. LINQ to SQL
's O/R mapper will then create a "Values" property for each Sample object, which contains a queryable collection of the attached Values. You would then use the following query:
var results =
from sample in myContext.Samples
from value in sample.Values
group value by value into values
orderby values.Key
select new { Value = values.Key, Frequency = values.Count() };

Related

Controlling LINQ query order based on other list of integers

I'm doing a LINQ query where I select the video info from table Videos. The query selects only those videos whose IDs are present in the following list:
List<int> results; //Has some values
var query = from l in dataContext.Videos
where results.Contains(l.ID)
select l;
Now how do I order the items(Video infos) in the query such the their IDs follow the same order as the List results?
I am able to do this as:
List<int> results; //Has some values
var query = from k in results
from l in dataContext.Videos
where k==l.ID
select l;
But this is slow, I need something faster.
Use a join, it's much faster
var orderedByIDList = from k in results
join l in dataContext.Videos
on k equals l.Id
select l;
Addon/Edit due to #MarcinJuraszek and #Phil comments, thanks guys.
Basically grab your data first, then sort so here's what I got:
var myList = (from l in dataContext.Videos
where results.Contains(l.ID)
select l).ToList(); //grab data and resolve to list or array
var orderedByIDList = from k in results
join l in myList
on k equals l.Id
select l; //result type IEnumerable<Video>
Here's my alternative attempt (probably not as fast as a join), which retrieves the minimum set of rows and then orders the data locally.
var results = new List<int>{ 9, 2, 3, 6, 8 };
// record the original order
var results2 = results.Select ((r, index) => new {r, index});
// get results and convert to list
var videos = dataContext.Videos.Where(v => results.Contains(v.Id)).ToList();
// order according to results order
var ordered = videos.Select (v =>
new {v, results2.Single (r => r.r == v.Id).index})
.OrderBy (v => v.index).Select (v => v.v)

linq-to-sql getting sequence contains more than one element

I have a query that looks like this: it takes a list of IDs (ThelistOfIDs) as parameter and I'm grouping for a count.
var TheCounter = (from l in MyDC.SomeTable
where ThelistOfIDs.Contains(l.ID)
group l by l.Status into groups
select new Counter()
{
CountOnes = (from g in groups
where g.Status == 1
select g).Count(),
CountTwos = (from g in groups
where g.Status == 2
select g).Count(),
}).Single();
And basically, I don't understand why I'm getting the error. I don't want to brring back the entore collection from the DB and do the count in linq-to-object; I want to do the count in the DB and bring back the result.
I have not put your query into my IDE or compiled with C#, but I guess the problem is that
groups in your query is IGrouping<Tkey, Telm> and not IQueryable<Tkey>
(where Tkey is type of l.Status and Telm is type of l).
I think you got confused with the use of grouping operator.
What you want to get is I guess:
var queryByStatus = from l in MyDC.SomeTable
where ThelistOfIDs.Contains(l.ID)
group l by l.Status;
var counter = new Counter()
{
CountOnes = queryByStatus.Where(l => l.Key == 1).Count(),
CountTwos = queryByStatus.Where(l => l.Key == 2).Count(),
};
EDIT:
Alternative query, to obtain the same, moving all operation on DB into the original query so that DB is queried only once.
var queryCountByStatus = from l in MyDC.SomeTable
where ThelistOfIDs.Contains(l.ID)
group l by l.Status into r
select new { status = r.Key, count = r.Count() };
var countByStatus = queryCountByStatus.ToList();
var counter = new Counter()
{
CountOnes = countByStatus.FirstOrDefault(l => l.status == 1).count,
CountTwos = countByStatus.FirstOrDefault(l => l.status == 2).count,
};
Note:
The query in my edit section queries the DB once only and mapping Status -> Count is returned.
Note that in my original query there were two calls to DB needed only - both of which returned single number - one for CountOnes, one for CountTwos.
In the edit query, one query is done which return table { { 1, CountOnes}, {2, CountTwos } }. The other lines are just to convert the result - which is set of items - into single object having certain objects as properties and is done physically on these two values.
You are grouping by Status, and then projecting from that group - but you will still have one row per unique Status (===group).
So: I propose that you don't have exactly one unique Status.
This might be what you're looking for to get...
(it's for users table I had but should be the same)
var statuscounts = (from u in db.Users
where u.UserStatus > 0
group u by u.UserStatus into groups
select new { Status = groups.Key, Count = groups.Count() });
// do this to iterate and pump into a Counter at will
foreach (var g in statuscounts)
Console.WriteLine("{0}, {1}", g.Status, g.Count);
...or even something like this...
var counter = statuscounts.AsEnumerable()
.Aggregate(new Counter(), (c, a) => {
switch (a.Status)
{
case 1: c.CountOfOnes = a.Count; return c;
case 2: c.CountOfTwos = a.Count; return c;
case 3: c.CountOfThrees = a.Count; return c;
default: c.CountOfOthers = a.Count; return c;
}});
...point is that if you're grouping already you should use the grouping result, it's of type IGrouping<out TKey, out TElement> where the key is your status and it's IEnumerable<> or your records.
hope this helps

c# group by doesn't work

i have some problems with my c# code everywhere in the Examples they do it like me but somehow i gonna get some errors
Compiler says at g.Datum he doesn' t know Datum
and at "return query" he says - cannot convert, there is a explicit convert
var query = (from p in dataContext.Untersuchungen
orderby p.Datum
group p by p.Datum into g
let number = (from n in dataContext.Untersuchungen
where n.Datum == g.Datum
select n).Count()
select new StatsistikObjekt() { Date1 = g.Datum, number1 = number });
return query;
hope you can help me =)
The type of the range variable g is the group, which indeed doesn't have a Datum value.
You can fix that bit easily, given your grouping (which uses Datum as the key)- and make your query simpler too by just counting the size of the group:
var query = (from p in dataContext.Untersuchungen
orderby p.Datum
group p by p.Datum into g
select new StatsistikObjekt() { Date1 = g.Key,
number1 = g.Count() });
As for the return value - we can't really help you on that one, as we don't know the return type you're trying to return.
Try
g.Key instead of g.Datum

Get min value in row during LINQ query

I know that I can use .Min() to get minimum value from column, but how to get minimum value in a row?
I have following LINQ query (for testing purposes):
from p in Pravidloes
where p.DulezitostId == 3
where p.ZpozdeniId == 1 || p.ZpozdeniId == 2
where p.SpolehlivostId == 2 || p.SpolehlivostId == 3
group p by p.VysledekId into g
select new {
result = g.Key,
value = g
}
Which results into this:
I would however like to get only the MIN value of following three columns:
DulezitostId, ZpozdeniId, SpolehlivostId as a value in:
select new {
result = g.Key,
value = g // <-- here
}
The final result then should look like:
result: 2, value: 1
result: 3, value: 2
I have been looking for similar questions here and googled for few examples with grouping and aggregating queries, but found nothing that would move me forward with this problem.
Btw: Solution isn't limited to linq, if you know better way how to do it.
You could create an array of the values and do Min on those.
select new {
result = g.Key,
value = g.SelectMany(x => new int[] { x.DulezitostId, x.ZpozdeniId, x.SpolehlivostId }).Min()
}
This will return the min for those 3 values in each grouping for ALL rows of that grouping.
Which would result in something like this...
result: 3, value: 1
The below will select the min for each row in the grouping...
select new {
result = g.Key,
value = g.Select(x => new int[] { x.DulezitostId, x.ZpozdeniId, x.SpolehlivostId }.Min())
}
Which would result in something like this...
result: 3, value: 1, 2
The best solution if you're using straight LINQ is Chad's answer. However, if you're using Linq To SQL it won't work because you can't construct an array like that.
Unfortunately, I believe the only way to do this in Linq To Sql is to use Math.Min repeatedly:
select new {
result = g.Key,
value = Math.Min(Math.Min(DulezitostId, ZpozdeniId), SpolehlivostId)
}
This will generate some ugly CASE WHEN ... statements, but it works.
The main advantage of doing it this way is that you're only returning the data you need from SQL (instead of returning all 3 columns and doing the Min in the application).

C# LINQ Query - Group By

I'm having a hard time understanding how I can form a LINQ query to do the following:
I have a table CallLogs and I want to get back a single result which represents the call that has the longest duration.
The row looks like this:
[ID] [RemoteParty] [Duration]
There can be multiple rows for the same RemoteParty, each which represents a call of a particular duration. I'm wanting to know which RemoteParty has the longest total duration.
Using LINQ, I got this far:
var callStats = (from c in database.CallLogs
group c by c.RemoteParty into d
select new
{
RemoteParty = d.Key,
TotalDuration = d.Sum(x => x.Duration)
});
So now I have a grouped result with the total duration for each RemoteParty but I need the maximum single result.
[DistinctRemoteParty1] [Duration]
[DistinctRemoteParty2] [Duration]
[DistinctRemotePartyN] [Duration]
How can I modify the query to achieve this?
Order the result and return the first one.
var callStats = (from c in database.CallLogs
group c by c.RemoteParty into d
select new
{
RemoteParty = d.Key,
TotalDuration = d.Sum(x => x.Duration)
});
callStats = callStats.OrderByDescending( a => a.TotalDuration )
.FirstOrDefault();
Have a look at the "Max" extension method from linq
callStats.Max(g=>g.TotalDuration);

Categories