i would translate this queries with extension method
also i would merge this queries in a single query with extension method
var variants = _ctx.Varianti.Where(i=>i.attivo==0);
var allProducts = await (from p in _ctx.Articoli
where p.cat==1
join v in variants on p.code equals v.code into gj
from articoli in gj.DefaultIfEmpty()
select new {
Codart = p.Codart,
Codvar = articoli.Codvar,
}).ToListAsync();
My classes
class Articolo{
public string Codart //key
public double price
}
class Variante{
public string Codart //key
public string Codvar // key
public int attivo
}
I have to return products like so
Prod1-Variant1
Prod2-(no variant)
prod3-Variant1
prod4-Variant1
prod4-Variant2
prod5-(no variant)
I should filters only variants with attivo==0
And all product without variant if they not have
The code works well but i need to optimize in single query to database
and also with extension method
In T-Sql should be as so:
SELECT Codart,
Codvar
FROM dbo.Articoli
LEFT OUTER JOIN dbo.Varianti
ON dbo.Articoli.Codart = dbo.Varianti.Codart
WHERE (Cat = 1)
AND (attivo = 0)
I'm still not sure what's the problem. Here some example how to "left-outer-join" the products and variants and select a new object.
List<Articolo> products = new List<Articolo>()
{
new Articolo() { Code = "1", price = 1 },
new Articolo() { Code = "2", price = 1 },
new Articolo() { Code = "3", price = 1 },
new Articolo() { Code = "4", price = 1 },
new Articolo() { Code = "5", price = 1 },
};
List<Variante> variants = new List<Variante>()
{
new Variante() { Code = "1", attivo = 0, Codvar = "v1" },
new Variante() { Code = "3", attivo = 0, Codvar = "v1" },
new Variante() { Code = "4", attivo = 0, Codvar = "v1" },
new Variante() { Code = "4", attivo = 0, Codvar = "v2" },
new Variante() { Code = "5", attivo = 1, Codvar = "v2" },
};
var result = products // Our "Left"-List
.GroupJoin( // Join to a "one"-to-"null or many"-List
variants.Where(v => v.attivo == 0), // Our "right"-List
p => p.Code, // select key in left list
v => v.Code, // select key in right list
(p, v) => // for every product "p" we have a list of variants "v"
v.Any() ? // do we have vriants for our product?
v.Select(s => new // for every variant we build our new product
{
Code = p.Code,
FirstVariant = s.Codvar,
})
: // if we got no variants, we build a "no variant"-product
new[] { new {
Code = p.Code,
FirstVariant = "No Variant"
} } // here we got a list of product-variants per product ("list of lists")
).SelectMany(s => s); // We want one list of all product variants
foreach (var item in result)
{
Console.WriteLine("Code: {0}, FirstVar: {1}", item.Code, item.FirstVariant);
}
Related
I have a strange question :)
I have a object list looking like this:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
I would like to sort the list providing a "start id"
For example, if I provide "start id" 3, the result should look like this:
Id
Name
3
Patric
4
Theodor
1
Marcus
2
Mattias
I have no idea where to start, so I really need some help from you coding gods
The list is from a sql table, but it does not matter for me where the sort take place (in sql query or in c# code)
Try this:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
var start_id = 3;
var max_id = list.Max(y => y.Id);
var result =
from x in list
orderby (x.Id + max_id - start_id) % max_id
select x;
I get:
With LINQ to objects you can do something like that:
var list = new []
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
var startId = 3;
var result = list
.GroupBy(i => i.Id >= startId ? 1 : 0) // split in two groups
.OrderByDescending(g => g.Key) // sort to have the group with startId first
.Select(g => g.OrderBy(i => i.Id)) // sort each group
.SelectMany(i => i) // combine result
.ToList();
Console.WriteLine(string.Join(", ", result.Select(i => i.Id))); // prints "3, 4, 1, 2"
You require 2 criteria to apply:
Order ascending by Id.
Return the Ids greater than threshold before the Ids less than threshold.
You can try:
var offset = 3;
var sorted1 = list
.OrderBy(item => item.Id < offset)
.ThenBy(item => item.Id);
The OrderBy condition yields true if Id is less than offset and false otherwise.
true is greater than false and therefore is returned later
A dirty way could also be:
var offset = 3;
var sorted2 = list
.OrderBy(item => unchecked((uint)(item.Id - offset)));
Here the offset is subtracted from Id and the result converted to unsigned int to make the negative values become very large positive ones. A little hacky. Might not work with queries against SQL providers.
Here's a toy Non-Linq Version
object[] ShiftList(int id)
{
var list = new dynamic[]
{
new { Id = 1, Name = "Marcus" },
new { Id = 2, Name = "Mattias" },
new { Id = 3, Name = "Patric" },
new { Id = 4, Name = "Theodor" },
};
Span<dynamic> listSpan = list;
int indexFound = -1;
for (int i = 0; i < list.Length; i++)
{
if (listSpan[i].Id == id)
{
indexFound = i;
}
}
if (indexFound is -1)
{
return list;
}
var left = listSpan.Slice(0, indexFound);
var right = listSpan[indexFound..];
object[] objs = new object[list.Length];
Span<object> objSpan = objs;
right.CopyTo(objSpan);
left.CopyTo(objSpan[right.Length..]);
return objs;
}
Try using foreach and iterate over each object in your list:
foreach (var item in list)
{
}
from here you should be able to use some of the collection methods for a list to reorder your list.
Given the following class:
public class Transaction
{
public string Category { get; set; }
public string Form { get; set; }
}
How do I get a grouping of transactions that are grouped by both the Category and the Form?
Basically I want it to output like so:
Category 1
Form 1
Transaction1
Transaction2
Transaction3
...
Form 2
Transaction1
Transaction2
Transaction3
...
Category 2
Form 1
Transaction1
Transaction2
Transaction3
...
Form 2
Transaction1
Transaction2
Transaction3
...
Here is an example using nested foreach loops, I'm not sure how you would do this in a single string of linq statements, maybe with lots of selectmanys?
var transactions = new[]{
new{Category = "1", Form = "1", Title = "Trans1" },
new{Category = "1", Form = "1", Title = "Trans2" },
new{Category = "1", Form = "1", Title = "Trans3" },
new{Category = "1", Form = "2", Title = "Trans1" },
new{Category = "1", Form = "2", Title = "Trans2" },
new{Category = "1", Form = "2", Title = "Trans3" },
new{Category = "2", Form = "1", Title = "Trans1" },
new{Category = "2", Form = "1", Title = "Trans2" },
new{Category = "2", Form = "1", Title = "Trans3" },
new{Category = "1", Form = "3", Title = "Trans1" },
new{Category = "1", Form = "3", Title = "Trans2" },
new{Category = "1", Form = "3", Title = "Trans3" },
};
foreach(var byCategory in transactions.GroupBy(x => x.Category))
{
Console.WriteLine(byCategory.Key);
foreach(var byForm in byCategory.GroupBy(x => x.Form))
{
Console.WriteLine("\t" + byForm.Key);
foreach(var trans in byForm)
{
Console.WriteLine("\t\t" + trans.Title);
}
}
}
Just because I was curious what it would look like I came up with the following, YOU SHOULD NOT USE THIS IN PRODUCTION CODE as it is ridiculous (if you do have a data structure like this it should be broken up into something like Dictionary<CategoryName, FormGroup> or something with meaningful types)
Dictionary<string, Dictionary<string, List<string>>> tooManyDictionaries = transactions
.GroupBy(x => x.Category)
.ToDictionary(
catGroup => catGroup.Key,
catGroup => catGroup
.GroupBy(x => x.Form)
.ToDictionary(
formGroup => formGroup.Key,
formGroup => formGroup.Select(x => x.Title).ToList()));
I ended up with the following, because the grouping need to be complete before the iteration over the collection.
Seed Some Transactions
var cats = new[] { "Category 1", "Category 2", "Category 3" };
var frms = new[] { "Form 1", "Form 2", "Form 3" };
var transactions = new List<Transaction>();
for (var i = 0; i <= 150; i++)
{
transactions.Add(new Transaction
{
Category = i % 2 == 0 ? cats[0] : i % 3 == 0 ? cats[1] : cats[2],
Form = i % 5 == 0 ? frms[0] : i % 7 == 0 ? frms[1] : frms[2]
});
}
The Grouping
var groupedTransactions = transactions.GroupBy(x => x.Category)
.Select(x => new
{
Category = x.Key,
Forms = x.ToList()
.GroupBy(y => y.Form)
});
Write it to the Console
foreach (var group in groupedTransactions.OrderBy(x => x.Category))
{
Console.WriteLine(group.Category);
foreach (var form in group.Forms.OrderBy(x => x.Key))
{
Console.WriteLine("\t" + form.Key);
foreach (var transaction in form)
{
Console.WriteLine("\t\t" + transaction.Id);
}
}
}
Basically, the first group should contain every piece of information that you will need to group by later, and then on repeat groupings, remove one piece of information that is relevant for that grouping level. Do this as many times as you need.
ValueTuple can help a lot, since it lets you have a composite key that can be passed to another type. Otherwise with anonymous types, you'll need to rely on type inference to pass the groups to something else. One issue with ValueTuple though is that you can't have a 1-Tuple for some reason, so in that case you need to group by the single property and not use a tuple.
If you already have a hierarchical relationship in your data structure, then grouping by a Tuple might be unnecessary.
var groups =
transactions
.GroupBy(tran => (
Category: tran.Category,
Form: tran.Form
)).GroupBy(group => group.Key.Form)
.ToList();
The type gets complicated very fast, so use type inference and refactoring tools to avoid having to figure out the specific type, when possible. For example, just the above results in the type:
List<IGrouping<string, IGrouping<(string Category, string Form), Transaction>>>
I'm having trouble with getting a my linq query correct. I've been resisting doing this with foreach loops because I'm trying to better understand linq.
I have following data in LinqPad.
void Main()
{
var events = new[] {
new {ID = 1, EventLevel = 1, PatientID = "1", CodeID = "2", Occurences = 0 },
new {ID = 2, EventLevel = 2, PatientID = "1", CodeID = "2", Occurences = 0 },
new {ID = 3, EventLevel = 1, PatientID = "2", CodeID = "1", Occurences = 0 },
new {ID = 4, EventLevel = 3, PatientID = "2", CodeID = "2", Occurences = 0 },
new {ID = 5, EventLevel = 1, PatientID = "3", CodeID = "3", Occurences = 0 },
new {ID = 6, EventLevel = 3, PatientID = "1", CodeID = "4", Occurences = 0 }
};
var filter = new FilterCriterion();
var searches = new List<FilterCriterion.Occurence>();
searches.Add(new FilterCriterion.Occurence() { CodeID = "1", MinOccurences = 2, MaxOccurences = 3 });
searches.Add(new FilterCriterion.Occurence() { CodeID = "2", MinOccurences = 2, MaxOccurences = 3 });
filter.Searches = searches;
var summary = from e in events
let de = new
{
PatientID = e.PatientID,
CodeID = e.CodeID
}
group e by de into t
select new
{
PatientID = t.Key.PatientID,
CodeID = t.Key.CodeID,
Occurences = t.Count(d => t.Key.CodeID == d.CodeID)
};
var allCodes = filter.Searches.Select(i => i.CodeID);
summary = summary.Where(e => allCodes.Contains(e.CodeID));
// How do I find the original ID property from the "events" collection and how do I
// eliminate the instances where the Occurences is not between MinOccurences and MaxOccurences.
foreach (var item in summary)
Console.WriteLine(item);
}
public class FilterCriterion
{
public IEnumerable<Occurence> Searches { get; set; }
public class Occurence
{
public string CodeID { get; set; }
public int? MinOccurences { get; set; }
public int? MaxOccurences { get; set; }
}
}
The problem I have is that need to filter the results by the MinOccurences and MaxOccurences filter property and in the end I want the "events" objects where the IDs are 1,2,3 and 4.
Thanks in advance if you can provide help.
To access event.ID at the end of processing you need to pass it with your first query. Alter select to this:
// ...
group e by de into t
select new
{
PatientID = t.Key.PatientID,
CodeID = t.Key.CodeID,
Occurences = t.Count(d => t.Key.CodeID == d.CodeID),
// taking original items with us
Items = t
};
Having done that, your final query (including occurrences filter) might look like this:
var result = summary
// get all necessary data, including filter that matched given item
.Select(Item => new
{
Item,
Filter = searches.FirstOrDefault(f => f.CodeID == Item.CodeID)
})
// get rid of those without matching filter
.Where(i => i.Filter != null)
// this is your occurrences filtering
.Where(i => i.Item.Occurences >= i.Filter.MinOccurences
&& i.Item.Occurences <= i.Filter.MaxOccurences)
// and finally extract original events IDs
.SelectMany(i => i.Item.Items)
.Select(i => i.ID);
This produces 1, 2 as result. 3 and 4 are left out as they don't get past occurrences filtering.
I have run your program in linqpad.
My understanding is that you want to filter using filter.MinOccurences and filter.MaxOccurences on Occurences count of result data set.
You can add additional filters using Where clause.
if (filter.MinOccurences.HasValue)
summary = summary.Where (x=> x.Occurences >= filter.MinOccurences);
if (filter.MaxOccurences.HasValue)
summary = summary.Where (x=> x.Occurences <= filter.MaxOccurences);
Given the following input, how do I write a LINQ query or expression to return an aggregated result set for the quantity?
Input:
var foo = new[] { new { PO = "1", Line = 2, QTY = 0.5000 },
new { PO = "1", Line = 2, QTY = 0.2500 },
new { PO = "1", Line = 2, QTY = 0.1000 },
new { PO = "1", Line = 2, QTY = -0.1000 }
}.ToList();
Desired result:
Something along the lines of
new { PO = "1", Line = 2, QTY = 0.7500 } // .5 + .25 + .1 + -.1
How would I write it for multiple lines as well (see the object model in foo)?
How about this:
var result = foo.GroupBy(x => x.Line)
.Select(g => new { PO = g.First().PO,
Line = g.Key,
QTY = g.Sum(x => x.QTY) });
In the case you just have one Line, just add a .Single() - result is an IEnumerable of the anonymous type defined when you set up foo.
Edit:
If both PO and Line should designate different groups (PO can have different values), they both have to be part of the group key:
var result = foo.GroupBy(x => new { x.PO, x.Line})
.Select(g => new {
PO = g.Key.PO,
Line = g.Key.Line,
QTY = g.Sum(x => x.QTY)
});
var query = (from t in foo
group t by new {t.PO, t.Line}
into grp
select new
{
grp.Key.PO,
grp.Key.Line,
QTY = grp.Sum(t => t.QTY)
}).ToList()
I have three domain objects:
Child, Classroom and ChildClassroom. Here are lists of each:
var childrens = new List<Child>() {
new Child() { ChildId = 1, FirstName = "Chris" },
new Child() { ChildId = 2, FirstName = "Jenny" },
new Child() { ChildId = 3, FirstName = "Dave" },
};
var classrooms = new List<Classroom>() {
new Classroom() { ClassroomId = 1, FullName = "Kindergarten" },
new Classroom() { ClassroomId = 2, FullName = "Elementary" },
new Classroom() { ClassroomId = 3, FullName = "Secondary" },
};
var childclassrooms = new List<ChildClassroom>() {
new ChildClassroom() { ClassroomId = 1, ChildId = 1 },
new ChildClassroom() { ClassroomId = 2, ChildId = 1 },
new ChildClassroom() { ClassroomId = 3, ChildId = 2 },
};
What I want is:
var childClassroomRelationships = new object[] {
new {
childid = 1,
classrooms = new object[] {
new { classroomId = 1, occupied = true },
new { classroomId = 2, occupied = true },
new { classroomId = 3, occupied = false }
},
...
};
What's the way to go about this in Linq?
You could do this:
var childClassroomRelationships = (
from child in children
select {
childid = child.ChildId,
classrooms = (
from classroom in classrooms
select new {
classroomId = classroom.ClassroomId,
occupied = childclassrooms.Any(
cc => cc.ChildId == child.ChildId),
// Since you wanted an array.
}).ToArray()
// Since you wanted an array.
}).ToArray();
What's very important here is that a join should not be used here, if it was, you would get inner join semantics, which would cause children who are not in any classrooms to not show up (which it seems you don't want from the example you gave).
Note that this will materialize all sequences because of the calls to ToArray.
Also, it is slightly inefficient, in that to check the occupancy, it has to reiterate the entire childclassroms sequence every time.
This can be improved by "indexing" the childclassrooms map for efficient lookup, like so:
IDictionary<int, HashSet<int>> classroommap = (
from mapping in childclassrooms
group mapping.ClassroomId by mapping.ChildId into g
select g).ToDictionary(g => g.Key, g => new HashSet<int>(g));
This will give you a map of HashSet<int> instances which you can look up the child in once you know the classroom. With that, the first query becomes:
var childClassroomRelationships = (
from child in children
select {
childid = child.ChildId,
classrooms = (
from classroom in classrooms
select new {
classroomId = classroom.ClassroomId,
occupied = classroommap.ContainsKey(child.ChildId) &&
classroommap[child.ChildId].
Contains(classroom.ClassroomId),
// Since you wanted an array.
}).ToArray()
// Since you wanted an array.
}).ToArray();
var kidsInClass = (
from kid in childrens
from c in classrooms
select new {
ChildID = kid.ChildId,
classrooms = (
from cc in childclassrooms
select new {
ClassroomID = c.ClassroomId,
Occupied = cc.ChildId == kid.ChildId
}).ToArray()
}).ToArray();