Getting Sum by looping a list within a list c# - c#

public class GRNMaster
{
public string ID { get; set; }
public string GRNNo { get; set; }
public List<GRNDetails> GRNDetails { get; set; }
}
public class GRNDetails
{
public string GRNID { get; set; }
public string ItemID { get; set; }
public string ItemType { get; set; }
public int RecevedQty { get; set; }
}
above classes contains some of the properties of GRN header class and Detail class. i Grn can consist of many items so that "List GRNDetails" is there to keep them.
I take a GRN List from a method which will store in the variable GrnList
public List<GRNMaster> GrnList
I have a list of Items IDs
public List<string> ItemIDList
In the controller I want to loop the ItemIDList (List ItemIDList) and get sum for that particular item based on the List
int ItemQty = 0;
foreach (var item in ItemIDList)
{
ItemQty = 0;
var ItemQty = //some code using GrnList
// rest of the programming code based on the
// item qty
}

Using LINQ
var totalQty = 0;
foreach (var item in ItemIDList)
{
var sumOfCurrentItem = GrnList.SelectMany(s => s.GRNDetails
.Where(f => f.ItemID == item)).Select(f => f.RecevedQty).Sum();
totalQty += sumOfCurrentItem ;
}
Or even a one liner replacement of the foreach loop (Credit goes to ReSharper :) )
int totalQty = ItemIDList.Sum(item => GrnList
.SelectMany(s => s.GRNDetails.Where(f => f.ItemID == item))
.Select(f => f.RecevedQty).Sum());

If I've understood your requirements correctly then this is what you need:
IEnumerable<int> query =
from grn in GrnList
from grnDetail in grn.GRNDetails
join itemID in ItemIDList on grnDetail.ItemID equals itemID
select grnDetail.RecevedQty;
int ItemQty = query.Sum();

Related

C# group by a column and form hierarchical data with other columns

I am trying to group a column and form the the rest of the columns as child, hierarchical data:
I am trying to group by Code and form the parent and child relationship from a flat list, below is the hierarchical data I am trying to form:
source list:
public class ItemAssignmentFlatList
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need to convert above flat list into below List of hierarchical data:
public class ItemInfo
{
public int Code { get; set; }
public string Type { get; set; }
public string Description { get; set; }
public List<TaxInfo> TaxPlan { get; set; }
}
public class TaxPlan
{
public int ItemCode{ get; set; }
public DateTime EffectiveDate{ get; set; }
public string Area{ get; set; }
public string TaxCode{ get; set; }
public string LocationId { get; set; }
}
Need hierarchical list with above flat data list with C# extension methods.
I have below code, but looking for clean code to reduce number of lines:
var items= results.GroupBy(x => new { x.Code, x.Type });
List<ItemInfo> result = new List<ItemInfo>();
foreach (var group in items)
{
var taxPlans = group.
Select(y => new TaxPlan
{
TaxArea = y.TaxArea,
ItemCode = y.ItemCode
});
var itemInfo= new ItemInfo
{
Code = group.FirstOrDefault().Code,
Type = group.FirstOrDefault().Type,
Description = group.FirstOrDefault().Description,
TaxPlan = taxPlans.ToList()
};
result.Add(itemInfo);
}
Something like this?:
var input = new List<ItemAssignmentFlatList>(){
new ItemAssignmentFlatList{
Code = 1,
Area = "a"
},
new ItemAssignmentFlatList{
Code = 1,
Area = "b"
},
new ItemAssignmentFlatList{
Code = 2,
Area = "c"
}
};
input
.GroupBy(
x => x.Code,
(int code, IEnumerable<ItemAssignmentFlatList> items) =>
{
var first = items.FirstOrDefault();
var key = new ItemInfo
{
Code = first.Code
//, ...
};
var plan = items.
Select(y => new TaxPlan
{
Area = y.Area
//, ...
});
return new
{
key = key,
items = plan
};
}
).Dump();
Whenever you have a sequence of similar object, and you want to make "Items with their SubItems", based on common properties in your source sequence, consider to use one of the overloads of Enumerable.GroupBy
Because you don't just want "Groups of source items" but you want to specify your output, consider to use the overload that has a parameter resultSelector.
parameter keySelector: what should all elements in a group have in common
parameter resultSelector: use the common thing, and all elements that have this common thing to make one output element.
.
IEnumerable<ItemAssignmentFlatList> flatItemAssignments = ...
IEnumerable<ItemInfo> items = flatItemAssignments
// make groups with same {Code, Type, Description}
.GroupBy(flatItemAssignment => new {Code, Type, Description},
// parameter resultSelector: take the common CodeTypeDescription,
// and all flatItemAssignments that have this common value
// to make one new ItemInfo
(codeTypeDescription, flatItemAssignmentsWithThisCodeTypeDescription) => new ItemInfo
{
Code = codeTypeDescription.Code,
Type = codeTypeDescription.Type,
Description = codeTypeDescription.Description,
TaxPlans = flatItemAssignmentsWithThisCodeTypeDescription
.Select(flatItemAssignment => new TaxPlan
{
ItemCode = flatItemAssignment.ItemCode,
EffectiveDate = flatItemAssignment.EffectiveDate,
Area = flatItemAssignment.Area,
...
})
.ToList(),
});

C# add anonymous list to typed list

I am a little new to C#, sorry if the question is rudimentary.
I have an anonymous list as result of the following:
var Totals = model.Payments.GroupBy(pm => pm.PaymentType)
.Select(t => new
{
PaymentType = t.Key,
Amount = t.Sum(pm => pm.Amount)
});
Returns Totals as anonymous list with two entries.
Cash 10000.00
EFT 8000.00
Now I need to add this list to a typed list in my viewmodel. The typed list looks like this
public class PaymentExportViewModel
{
public List<PaymentReportViewModel> Payments { get; set; }
public List<PaymentSubTotals> Subs { get; set; }
public DateTime FromDate { get; set; }
public DateTime ToDate { get; set; }
public String VenueName { get; set;}
public PaymentExportViewModel()
{
Payments = new List<PaymentReportViewModel>();
Subs = new List<PaymentSubTotals>();
}
}
public class PaymentSubTotals
{
public string Type { get; set; }
public decimal Amount { get; set; }
}
So since I need to "convert" to typed I do this
PaymentSubTotals subs = new PaymentSubTotals();
foreach (var t in Totals)
{
subs.Type = t.PaymentType;
subs.Amount = t.Amount;
model.Subs.Add(subs);
}
The result is that model.subs now contains 2 entries but both are the same (last entry in my loop)
EFT 8000.00
EFT 8000.00
What am I missing in the model.Subs.Add ? Or somewhere else ?
Move declation of subs inside of foreach loop:
foreach (var t in Totals)
{
PaymentSubTotals subs = new PaymentSubTotals();
subs.Type = t.PaymentType;
subs.Amount = t.Amount;
model.Subs.Add(subs);
}
In your code you change the same sub object every time, so you do have only the data of the last t variable, repeated.
Why don't you Select into your PaymentSubTotals instead.
var Totals = model.Payments.GroupBy(pm => pm.PaymentType)
.Select(t => new PaymentSubTotals
{
Type = t.Key,
Amount = t.Sum(pm => pm.Amount)
});
I would recommend the following method to add a new PaymentSubTotals object on each loop.
foreach (var t in Totals)
{
model.Subs.Add(new PaymentSubTotals {
Type = t.PaymentType;
Amount = t.Amount;
});
}

Linq "join" with a IList<T> getting "Error Unable to create a constant value.."

I have a Save Method that saves with a Linq query a manually re-orderd list (in a web form) that is passed as the parameter to my method, and I try to update the Order Property of the IEnumerable<VM_CategoryLabel> I retrieve from the database (EF) with the corresponding value in the list (maybe would that be clearer with my code below):
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
//Order = x.Sequence_Cat,
Order = (int)listTemplate.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
I used the "test" var to see if my "sub-query" gets the correct value, and it does, but when I use my Linq expression inside the Select (the commented Order line), I get the following error:
Unable to create a constant value of type 'Namespace.Models.VM_CategoryLabelExtra. "Only primitive types and enumeration types are supported in this context.
Here are my classes:
public class VM_CategoryLabel
{
public int Id { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
public class VM_CategoryLabelExtra
{
public int Id { get; set; }
public int IdCat { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
So I suppose that I should not query the list inside my query ? So how do I "match" the 2 lists of values ?
I also tried the following (after having replace in the Linq query: Order = x.Sequence_Cat)that is not working neither because the iteration variable is
read-only:
foreach (var item in requete)
{
item.Order = listTemplate.Where(x => x.Id == item.Id).Select(x => x.Order).FirstOrDefault();
}
try
{
context.SaveChanges();
I suggest using this.
It is the let clause.
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
let list = listTemplate
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
Order = list.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
edit: instead offrom you can just do let list = listTemplate
Should work now :)
example for let:
// The let keyword in query expressions comes in useful with subqueries: it lets
// you re-use the subquery in the projection:
from c in Customers
let highValuePurchases = c.Purchases.Where (p => p.Price > 1000)
where highValuePurchases.Any()
select new
{
c.Name,
highValuePurchases
}
If you do not know how Let working than please download LinqPad and see an example

Complex graphing in entity framework

I can't seem to find the correct where clause to get only the items I need.
I have Divisions, these contain Categories en these contain Items.
These are the classes:
public class Division {
public string Description { get; set; }
public List<Category> Categories { get; set; }
}
public class Category : IdEntity
{
public string Description { get; set; }
public Guid DivisionId { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public Guid CategoryId { get; set; }
public DateTime Date { get; set; }
public string Description { get; set; }
}
What I need is a division with the Id in a parameter, all the categories from this division and the items that have a certain date for each category.
So right now I do this:
public Division GetFullDivisionByIdAndDate(Guid id, DateTime date)
{
using (new ChangeTrackingScope(ChangeTracking.Disabled))
{
var divisionGraph = new Graph<Division>().Include(d => d.Categories.Select(c => c.Items));
var division = _divisionDL.GetFullDivisionByIdAndDate(id, divisionGraph, date);
return division;
}
}
And than in the DL I do
public Division GetFullDivisionByIdAndDate(Guid id, Graph<Division> graph, DateTime date)
{
using (var db = new ContextScope<DatabaseContext>())
{
var q = graph.ApplySetReferences(db.Context.Divisions).AsNoTracking();
return q.SingleOrDefault(p => p.Id == id);
}
}
Here I get the division with all its categories (so far so good) but I also get all items and I need only the items with the date given as parameter. Anyone has an idea how to do this?
Your code is not very accessible because of a few missing methods (Graph, ApplySetReferences) so I can't hook into it. But I can show a common way to query an object graph, which is by navigation properties. In you model, a basic query body could look like this:
from d in Divisions
from c in d.Categories
from i in c.Items
select new { Div = d.Description, Cat = c.Description, Item = i.Description }
Starting from here you can add other filters and properties, like
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
from i in c.Items.Where(item => item.Date == date)
select new { ... }
Or if you want a result with items in a filtered collection:
from d in Divisions.Where(div => div.Id == id)
from c in d.Categories
select new new { Div = d.Description,
Cat = c.Description,
Items = c.Items.Where(item => item.Date == date)
}

LINQ to Lookup with distinct items and count

If I define a list as
public class ItemsList
{
public string structure { get; set; }
public string Unit { get; set; }
public double Dim { get; set; }
public double Amount { get; set; }
public int Order { get; set; }
public string Element { get; set; }
}
List<ItemsList> _itemsList = new List<ItemsList>();
I'm trying to get the a distinct count of structures in a Lookup with structure as the key and count of structure as the value.
At the moment I have
var sCount = from p in _itemsList
group p by p.Structure into g
select new { Structure = g.Key, Count = g.Count() };
but this just returns the data as an anonymous type. Can someone please help me with the syntax to get this into a Lookup using .ToLookup?
I suspect you actually want:
var lookup = _itemsList.ToLookup(p => p.Structure);
You can still count the items for any group:
foreach (var group in lookup)
{
Console.WriteLine("{0}: {1}", group.Key, group.Count());
}
... but you've also got the values within each group.
If you really just want the count, then it sounds like you don't want a lookup at all - you want a Dictionary, which you can get with:
var dictionary = _itemsList.GroupBy(p => p.Structure)
.ToDictionary(g => g.Key, g => g.Count());

Categories