C# LINQ Query - c#

I have the following query:
var results = from theData in GeometricAverage
group theData by new { study = theData.study, groupNumber = theData.groupNumber, GeoAverage= theData.GeoAverage } into grp
select new
{
study = grp.Key.study,
groupNumber = grp.Key.groupNumber,
TGI = testFunction(grp.Key.GeoAverage, Also here I want to pass in the GeoAverage for only group 1 (but for each individual study))
};
What I want to do is that for each study, there are multiple groups with a GeoAverage figure for each group. The TGI is calculated by passing the GeoAverage figure for each group and the GeoAverage figure for group 1 (on each study) into the testFunction. I can't figure out how to pass in the value just for group 1.
Hope this makes sense.
EDIT: Sample of data:
Study Group GeoAverage
1 1 3
1 2 5
1 3 6
2 1 2
2 2 3
2 3 9
So, for the above data, I would want each GeoAverage figure for each group, to be evaluated against the GeoAverage figure of group 1 within that same study. So if I have say a function:
int foo(int a, int b)
{
return a * b;
}
Using the data above, I would first evaluate study 1, group 1 against itself, so pass in GeoAverage 3 twice and return 9. For Study 1, group 2, pass in group 2 GA at 5, and that studys group1 GA at 3, returning 15.

Have now worked it out. I iterate through a collection of data that I want the value to be stored against and use the following two LINQ queries:
foreach (var data in compoundData)
{
var controlValue = from d in GeometricAverage
where d.study == data.study
where d.groupNumber == "1"
select d.GeoAverage;
var treatmentValue = from l in GeometricAverage
where l.study == data.study
where l.groupNumber == data.groupNumber
select l.GeoAverage;
data.TGI = CalculateTGI(controlValue, treatmentValue);
}

Related

LINQ to JSON group query on array

I have a sample of JSON data that I am converting to a JArray with NewtonSoft.
string jsonString = #"[{'features': ['sunroof','mag wheels']},{'features': ['sunroof']},{'features': ['mag wheels']},{'features': ['sunroof','mag wheels','spoiler']},{'features': ['sunroof','spoiler']},{'features': ['sunroof','mag wheels']},{'features': ['spoiler']}]";
I am trying to retrieve the features that are most commonly requested together. Based on the above dataset, my expected output would be:
sunroof, mag wheels, 2
sunroof, 1
mag wheels 1
sunroof, mag wheels, spoiler, 1
sunroof, spoiler, 1
spoiler, 1
However, my LINQ is rusty, and the code I am using to query my JSON data is returning the count of the individual features, not the features selected together:
JArray autoFeatures = JArray.Parse(jsonString);
var features = from f in autoFeatures.Select(feat => feat["features"]).Values<string>()
group f by f into grp
orderby grp.Count() descending
select new { indFeature = grp.Key, count = grp.Count() };
foreach (var feature in features)
{
Console.WriteLine("{0}, {1}", feature.indFeature, feature.count);
}
Actual Output:
sunroof, 5
mag wheels, 4
spoiler, 3
I was thinking maybe my query needs a 'distinct' in it, but I'm just not sure.
This is a problem with the Select. You are telling it to make each value found in the arrays to be its own item. In actuality you need to combine all the values into a string for each feature. Here is how you do it
var features = from f in autoFeatures.Select(feat => string.Join(",",feat["features"].Values<string>()))
group f by f into grp
orderby grp.Count() descending
select new { indFeature = grp.Key, count = grp.Count() };
Produces the following output
sunroof,mag wheels, 2
sunroof, 1
mag wheels, 1
sunroof,mag wheels,spoiler, 1
sunroof,spoiler, 1
spoiler, 1
You could use a HashSet to identify the distinct sets of features, and group on those sets. That way, your Linq looks basically identical to what you have now, but you need an additional IEqualityComparer class in the GroupBy to help compare one set of features to another to check if they're the same.
For example:
var featureSets = autoFeatures
.Select(feature => new HashSet<string>(feature["features"].Values<string>()))
.GroupBy(a => a, new HashSetComparer<string>())
.Select(a => new { Set = a.Key, Count = a.Count() })
.OrderByDescending(a => a.Count);
foreach (var result in featureSets)
{
Console.WriteLine($"{String.Join(",", result.Set)}: {result.Count}");
}
And the comparer class leverages the SetEquals method of the HashSet class to check if one set is the same as another (and this handles the strings being in a different order within the set, etc.)
public class HashSetComparer<T> : IEqualityComparer<HashSet<T>>
{
public bool Equals(HashSet<T> x, HashSet<T> y)
{
// so if x and y both contain "sunroof" only, this is true
// even if x and y are a different instance
return x.SetEquals(y);
}
public int GetHashCode(HashSet<T> obj)
{
// force comparison every time by always returning the same,
// or we could do something smarter like hash the contents
return 0;
}
}

Insert number 1-2 but not 2-1

I have (1,2,3) and i want insert in my table:
1-2
2-3
But I want exclude 2-1 and 3-2.
Any help?
First get the cartesian product of the items. Then exclude those with difference more than 1 and first number >= second number. Like this:
List<int> list = new List<int>(){1, 2, 3};
var result = from n1 in list
from n2 in list
select new {n1, n2};
result = result.Where(n=> (n.n1 < n.n2) && (n.n2 - n.n1 == 1)).ToList();
This will give you what you want:
Results

max amount of same item in a list with C#

Let's say I have this list:
1
1
1
1
2
2
2
3
I want to narrow it down with C# to a list with a maximum of two same items in a list so it would look like this:
1
1
2
2
3
I used to use 'distinct' like this:
string[] array = System.IO.File.ReadAllLines(#"C:\list.txt");
List<string> list = new List<string>(array);
List<string> distinct = list.Distinct().ToList();
but don't have an idea on how it could bring a max number of same values
You could do it with Linq as follows.
var Groups = Input.GroupBy( i => i );
var Result = Groups.SelectMany( iGroup => iGroup.Take(2) ).ToArray();

In linq to sql how to apply && condition with a variable size or array

I have the below table AttachmentToTagMappings
And I have an array which contains some tag id as shown below and the size can change in every execution.
int[] tagIds= new int[] {1,2,9}; //again the number of elements can change in every execution for example in next execution it may be {1,2,7,12} because the this time the user is looking for the file(s) which are/is mapped to 1,2,7 and 12
Now I want to write a linq by which I can get fileId(s) which are mapped to the above array tagIds elements. i.e. in above table FileId 201 is mapped to 1,2 and 9 so retrieve it but not 202 cause its only mapped to 1 and 2.
Solution
To solve this problem, I would make use a couple of LINQs like GroupBy, let and Except.
Here is my solution:
var grp = context.AttachmentToTagMappings.GroupBy(x => x.TagId);
int[] tagIds = new int[] {1,2,9};
var fileIds = from g in grp
let grpFileIds = g.Select(y => y.FileId)
let remFileIds = tagIds.Except(grpFileIds)
where remFileIds.ToList().Count == 0
select g.Key;
Or,
int[] tagIds = new int[] {1,2,9};
var fileIds = from attm in context.AttachmentToTagMappings
group attm by new {attm.TagId} into g
let grpFileIds = g.Select(y => y.FileId)
let remFileIds = tagIds.Except(grpFileIds)
where remFileIds.ToList().Count == 0
select g.Key;
Assumption: attm.TagId type is the same as tagId, that is, an int
Input-Output Results
{1, 2} -> 201, 202
{1, 2, 9} -> 201
{1, 2, 7} -> 202
{1, 2, 7, 9} -> nothing
Explanation
Using GroupBy you could first group your AttachmentToTagMappings based on its TagId thus creating two groups, one identified by 201 and having three attm members whose tag ids are 1, 2, and 9 respectively. Another group has key of 202 with four attm members whose tag ids are 1, 2, 7, and 12 respectively.
Using let we want to create an IEnumerable<int> grpFileIds for each group. grpFileIds contains only the group's file ids
Next, Using Except we want to omit all the tagId in tagIds which is contained in grpFileIds, resulting in the remaining remFileIds
If the remFileIds.Count is zero, it means all items in tagIds has a pair in the grpFields. Otherwise, if the remFileIds.Count is greater than zero, meaning at least there is one tagId which the group does not have
Select all keys in the group (that is 201, 202, etc) whose remFileIds.Count is zero.
Contains should be translated to an IN clause in SQL which seems to be what you want:
where tagIds.Contains(attm.TagId)
try .Contain() :
var attachments = from attm in context.AttachmentToTagMappings
where tagIds.Contains(attm.TagId)
select new { attm.FileId};
Or you can use || instead of && but i think .Contains() better less code;)
As I understand your question, you need to get a list of attachments which its tagId exists in an array.
var attachments = from attm in context.AttachmentToTagMappings
where
tagIds.Any(id=>id==attm.TagId)
select new { attm.FileId};
I think this is what you are looking for:
var ids = {1, 2, 3};
var files = context.Files.Where(file => ids.Contains(file.TagId));
if(!ids.AsEnumerable().Except(files.Select(f => f.TagId).AsEnumerable()).Any()){
return files;
}
return null;
Select all files and check that there aren't any ids that don't match to an id in the files and return list of files. If not then return null.
Syntax may not be exact as not tested.

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

Categories