Linq : Group by and sum based on another list - c#

I have list with below data (fetched from Json column)
public class ProductType
{
public string FieldName{ get; set; }
public List<string> items { get; set; }
}
List contains
Cart item1
item2
item3
wish item4
item2
item6
I have an another List which contains values related to ProductType
public class ProductCost
{
public string item { get; set; }
public int Amount{ get; set; }
}
List contains
item1 50
item2 60
I need to find sum of each item based on ProductTypes' FieldName column
Now I want to retrieve something like below
A list contains
cart sum(Amount of each items eg:110)
wish sum(amount of each items eg:60)
I'm sorry, I'm new to LINQ. Can someone help me or share resources where I can get a hint to achieve this? With a for loop I achieved this .But is there any way to find in one linq execution

You can use following query using Join, GroupBy and Sum:
List<ProductType> allProductTypes = new List<ProductType>(); // fill
List<ProductCost> allProductCosts = new List<ProductCost>(); // fill
var costsQuery = from pt in allProductTypes
from ptItem in pt.items
join pc in allProductCosts on ptItem equals pc.item
group (pt, ptItem, pc) by pt.FieldName into fieldGrp
select (FieldName: fieldGrp.Key, SumAmount: fieldGrp.Sum(x => x.pc.Amount));
List<ProductCost> totalCosts = costsQuery
.Select(x => new ProductCost {item = x.FieldName, Amount = x.SumAmount})
.ToList();

Related

Entity Left Join to Replace SQL

I am accustomed to using SQL left joins to get a list of all available options and determine what items are currently selected. My table structure would look like this
Table MainRecord -- recordId | mainRecordInfo
Table SelectedOptions -- recordId | optionId
Table AvailableOptions -- optionId | optionValue
and my query would look like this
SELECT optionValue, IIF(optionId IS NULL, 0, 1) AS selected
FROM AvailableOptions AS a
LEFT JOIN SelectedOptions AS s ON s.optionId = a.optionId AND a.recordId = 'TheCurrentRecord'
I am trying to replace this with Entity Framework, so I need help with both a model and a query -- they both need corrected.
public class MainRecord
{
public int recordId { get; set; }
public string mainRecordInfo { get; set; }
[ForeignKey("recordId")]
public List<SelectedOptions> selectedOptions { get; set; }
}
public class SelectedOptions
{
public int recordId { get; set; }
public int optionId { get; set; }
}
public class AvailableOptions
{
public int optionId { get; set; }
public string optionValue { get; set; }
}
Query
IQueryable<AvailableOptions> options = from o in context.AvailableOptions select o;
I can get a list of AvailableOptions, but how can I get a list and know which ones are selected?
If the number of selections and available options is small enough, you can do this in memory:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s });
selected will now be a list of objects with Available and Selected as properties and will only contain those that matched in optionId value.
If you only wish to get a pure list of AvailableOptions that match, simply chain a Select to the join:
var selected = options.Join(record.selectedOptions, ao => ao.optionId, so => so.optionId, (a, s) => new { Available = a, Selected = s })
.Select(o => o.Available);
Not a complete answer, but it is really good to understand the navigational properties that you get from the model. Here is a query that most likely isn't exactly what you want but that demonstrate it
from ao in _db.AvailableOptions
where ao.recordId == "TheCurrentRecord" && ao.SelectedOptions.OptionId == 1
select new
MyPoxo {ao.SelectedOptions.Value ?? 0};
so instead of just having o you navigate through the joins that gets specified by the FKs. In this example I would assum AvailableOptions would have a linke to SelectedOptions.

Select element in linq and change properties

Lets say I have two lists objects with this classes
class Sku
{
public string skuId { get; set; }
public int qty { get; set; }
public string city { get; set; }
}
class SkuWithCity
{
public string skuId { get; set; }
public string city { get; set; }
}
And I have two lists with objects:
List<Sku> skuList = new List<Sku>();
List<SkuWithCity> skuListWithCity = List<SkuWithCity>();
Imagine that in the first list(skuList), the property "City" of each object is null.
What I want to do is, using linq, select the sku objects that have the same skuId and add the city. Somethis like:
var result = from skuElements in skuList
from skuWCity in skuListWithCity
where skuElements.sku == skuWCity.sku
select skuElements
{
skuElements.city = skuWCity.city,
};
And get the whole object, not just the city
int order to get:
|Object | Qty | City
|---- |----|
|AAA | 2 | Panama|
|BBB | 5 | Rio De Janeiro|
is this even possible, get the whole object and modify one or many properties?
UPDATE: In real life the object that I'm trying to copy has a lot of members, that is why I'm trying to "copy" de object of list A and just modify some attributes using the match object of list B.
If you just want to update the existing objects instead of projecting to a new set of objects then first use a join to get the matching items.
var result = from skuElement in skuList
join skuWCity in skuListWithCity
on skuElements.skuId == skuWCity.skuId
select skuElements
{
skuElement,
skuWCity
};
Then iterate them and do the update.
foreach(var x in result)
{
x.skuElement.City = x.skuWCity.City;
}
Note this does not handle the case where more than one item in either list has more than one match in the other. I'm assuming that the lists have a one-to-one match already.
Alternatively you could just use a dictionary.
var cities = skuListWithCity.ToDictionary(s => s.skuId, s => s.City);
foreach(var s in skuList)
{
s.City = cities[s.skuId];
}
Note that this fails if there are duplicate skuId in skuListWithCity or if a skuId in skuList is not in skuListWithCity
You could use a join and then make a projection of the result set as below:
var result = from skuElements in skuList
join skuWCity in skuListWithCity
on skuElements.skuId equals skuWCity.skuId
select new Sku
{
skuId = skuElements.skuId,
qty = skuElements.qty,
city = skuWCity.city,
};

Select grouped data and bind it to listview/gridview best practice

What would be the best way if you have grouped data for example : (I have simplyfied everything for this example)
Item category table:
itemId itemCategoryId itemName
1 1 item1
2 1 item2
3 2 item3
4 1 item4
5 3 item5
and category table
itemCategoryId itemCategory
1 category1
2 category2
3 category3
So if I select all items I will get result same as first table(maybe with itemCategory ...)
but when I bind everything in Listview/Gridview or enything else I want to group items in categorys so if I have the same data as above my result in Listview/gridview ... should look like :
category1:
1 item1
2 item2
4 item4
category2:
3 item3
category3:
5 item5
What do you think is the best way to do this. I am using sql-server 2008 and visual studio 2010 ASP.NET c#. Thanks for your help
I wan`t to have result from database suitable for this class:
public class Item
{
public int itemId { get; set; }
public string itemName { get; set; }
public Item() { }
public Item(int itemId, string itemName)
{
this.itemId = itemId;
this.itemName = itemName;
}
}
public class GroupOfitems
{
public string itemCategory{ get; set; }
public List<Item> itemList { get; private set; }
public GroupOfitems()
{
itemList = new List<Item>();
}
public GroupOfitems(string itemCategory, List<Item> itemList)
{
this.itemCategory= itemCategory;
this.itemList = itemList;
}
}
so I can save the data from above tables in GroupOfItems class...
I would recommend use of the ListView, there is more support for grouping. In your SELECT query you should group rows by 'itemCategory'. See this tutorial for details of how to declare the ListView in your code.

C# Looking for a sortable array that handles both ints and strings

I'm not really sure if what i'm looking for actually exists, so maybe you guys can help out.
I have the below data:
Apples|3211|12
Markers|221|9
Turtle|1023123123|22
The first column is always a string, the second column and third column are ints. However, what I want to do is be able to reference theses as strings or ints, and then be able to sort via the third column asc. Any ideas?
Something like MyTable[i].Column[i] and in this case MyTable[1].Column[2] would produce 12 as a int (because it's ordered).
If you want type safety you will need to create a class that holds each record:
class Record
{
string Name { get; set; }
int SomeValue { get; set; }
int OrderNr { get; set; }
}
Afterwards store them in a generic List<>, then you can order them, as you like:
List<Record> items = // read them into a list of items;
List<Record> orderedList = items.OrderBy(i => i.OrderNr).ToList();
UPDATE
Since it was requested I customized the answer from JustinNiessner to fit to my example:
string data = // your data as string
List<Record> records = data
.Split('|')
.Select(item => new Record
{
Name = item[0],
SomeValue = int.Parse(item[1]),
OrderNr = int.Parse(item[2])
}).ToList();
List<Record> orderedRecords = records.OrderBy(r => r.OrderNr).ToList();
This can be optimized by using var and not executing ToList() on the list, but is done this way in order to keep it simple for you to understand the concepts better.
Assuming you have your data stored in some sort of IEnumerable<string> type, you could try something like:
var sortedObjs = stringRows
.Split('|')
.Select(r => new
{
ColA = r[0],
ColB = int.Parse(r[1]),
ColC = int.Parse(r[2])
})
.OrderBy(r => r.ColC).ToList();
var specificVal = sortedObjs[1].ColC;
This speaks to a larger problem in your design. Using collections to hold a bunch of disparate types with the intent of organizing them into some sort of structure is fragile, error prone, and completely unnecessary.
Instead, create your own type to organize this information.
class MyType
{
public string Name { get; set; }
public int Whatever { get; set; }
public int AnotherProp { get; set; }
}
Now your data is logically grouped in a nice, tight, type safe package.
Your original post didn't specify what the ints were, but since you wanted to select them either by the Descripton(?) or the Id(?) and then sort them by the third column perhaps something like this will work for you?
//Code tested in LinqPad
void Main()
{
//Apples|3211|12
//Markers|221|9
//Turtle|1023123123|22
//Create a list of items
var items = new List<Item>
{
new Item { Description = "Apple", Id = 3211, Sequence = 12 },
new Item { Description = "Markers", Id = 221, Sequence = 9 },
new Item { Description = "Turtle", Id = 1023123123, Sequence = 22 }
};
//Get sorted list of Apple by Description
var sortedByDescription = items.Where(i => i.Description == "Apple").OrderBy(i => i.Sequence);
//Get sorted list of Turtle by Id
var sortedById = items.Where(i => i.Id == 221).OrderBy(i => i.Sequence);
}
public class Item
{
public string Description { get; set; }
public int Id { get; set; }
public int Sequence { get; set; }
}

Linq query need correct sum

I am using .NET 4 Data Visualization Charts and am using a Linq query to get the data for the chart. I am new to Linq so not sure on how to word this query. Here is what it does, I have the follwoing tables its pulling data from
Orders
ShipmentSchedule
The orders table has the Product that was ordered, the OrderId, the amount that was shipped and the remaining quantity for that order. One order can only have a single product in it. I am trying to get all the Ordershipments in the class OrderShipment, make a List<> for it and group it by each product and show the total quantity for each product that was ordered and what has been shipped. The query below sums up the quantity shipped for all orders which is correct but because it is making a list of OrderShipments, I want the remaining quantity for each order across all shipments and sum that up by the product. Right now its adding up the remaining quantity for all the shipments which is wrong since an Order has the same Remaining Quantity across all shipments. how can I get the remaining quantity for each product so thw query adds up the Remaining quantity by each order correctly?? Please provide an example with code how to accomplish this if you have a suggestion, your helps really appreciated, thanks
private class ChartDataPoint
{
public string ProductName { get; set; }
public double QtyRemaining { get; set; }
public double QtyShipped { get; set; }
}
private class OrderShipment
{
public string ProductName { get; set; }
public double QuantityRemaining { get; set; }
public double QuantityShipped { get; set; }
public int OrderId { get; set; }
}
List<OrderShipment> productsOrdered =
(from os in Statistics.OptimalShipments
from o in Statistics.OrdersPlaced
where ((os.Date >= Statistics.ShippingWindowStartDate) &&
(os.Date <= Statistics.ShippingWindowEndDate) &&
(os.OrderId == o.OrderId) &&
((o.ClientLocationId == ClientLocation.ClientLocationId)))
select new OrderShipment()
{
ProductName = o.Product.Name,
QuantityRemaining = o.RemainingQuantity,
QuantityShipped = os.QuantityShipped,
}).ToList();
var query = productsOrdered.GroupBy(p => p.ProductName);
List<ChartDataPoint> chartDataPoints = new List<ChartDataPoint>();
foreach (var productGroup in query)
{
chartDataPoints.Add(new ChartDataPoint()
{
ProductName = productGroup.Key,
// This is obv wrong this sums up the Remaining quantity across
// all shipments for a order when we should be only taking the
//Remaining quantity once for each order across all shipments.
QtyRemaining = productGroup.Sum(po => po.QuantityRemaining),
QtyShipped = productGroup.Sum(po => po.QuantityShipped)
});
}
As I understand it your productGroup should have a lot of OrderShipment objects grouped by their product name. You then want to sum the QuantityShipped and the QuantityRemaining should all be the same and you want to just take this value?
If so then this should do you:
QtyRemaining = productGroup.First();
If I have misunderstood you may want to simplify the explanation, perhaps with an example...

Categories

Resources