I have the prices of the items in an array:
double[] productPriceArray = { 1162.99, 399.99, 329.99, 199.99, 149.99 };
I am trying to find the total of the ones that a user puts in a "cart". I'm not sure how to go about that. I know I can use the line of code:
subtotal = productCostArray[lbxCart.SelectedIndex];
lblSubtotal.Text = subtotal.ToString("c");
to find the total of one of the indices, but how can I find the total of multiple indices?
Thanks!
There is no way in the current design , You need atleast a list of product and product price mapping then you need a list of products in the cart and in the cart class you can define a function to get the subtotal of all products.
public class Product
{
public string ProductId { get; set; }
public string ProductName { get; set; }
public double Price { get; set; }
}
public class ShoppingCart
{
public string CartId { get; set; }
public List<Product> Products { get; set; }
public void AddProductToCart(Product p)
{
if(Products==null)
Products = new List<Product>();
if(p!=null)
Products.Add(p);
}
public double CartPrice()
{
return Products != null ? Products.Sum(p => p.Price):0D;
}
}
and usage
var shopCart = new ShoppingCart();
shopCart.AddProductToCart(new Product {ProductId = "1",Price = 12.09, ProductName = "P1"});
shopCart.AddProductToCart(new Product {ProductId = "2",Price = 11.09, ProductName = "P2"});
MessageBox.Show(shopCart.CartPrice().ToString());
If you have a list of selected item indices (as your example appears to suggest), you could do:
var subtotal = SelectedIndices.Select(idx => productPriceArray[idx]).Sum();
Related
I've seen the scenarios where a ViewModel is populated with one LINQ query as shown below. Question: How can I populate the TotalSale attribute (column) - that is a grand total of Sale column - of the following ViewModel? Note: I'm using latest version of ASP.NET Core with VS2015.
ViewModel:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
public float TotalSale { get; set; }
}
Controller:
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel { Category = category.Name, ProductName = prod.Name, Sale = prod.Sale };
You will have many of these in your view:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
public real TotalSale { get; set; }
}
But you will only have the last property TotalSale once. Therefore, change your view model to this:
public class CategProdViewModel
{
public string Category { get; set; }
public string ProductName { get; set; }
public float Sale { get; set; }
}
Create this model for your view and pass it to your view.
public class UseThisOnYourView // Give it another name
{
public List<CategProdViewModel> Items { get; set; }
public real TotalSale { get { return this.Items.Sum(x => x.Sale); } }
}
Then your query will be like this:
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel { Category = category.Name, ProductName = prod.Name, Sale = prod.Sale };
var model = new UseThisOnYourView
{
Items = innerJoinQuery.ToList()
};
NOTE: Please keep in mind you will need to adjust your view to use the new type you are passing to it and adjust the code accordingly.
You don't clarify the structure of Product, but you can Sum multiple values
var innerJoinQuery = from category in categories
join prod in products on category.ID equals prod.CategoryID
select new CategProdViewModel {
Category = category.Name,
ProductName = prod.Name,
Sale = prod.Sale,
TotalSales = products.Sum(t => t.Sale)
};
Without more about the code, it's hard to say, and also, the prod.sale is probably only going to be the amount of the last product. You can use Sum(this IEnumerable<TSource> source, Func<TSource, double> selector) to sum the total of multiple items.
This is a quick answer. It will probably need to be altered depending on what the rest of your code looks like. You can also Group the query results to sum.
I have a class structure as below whereby I have a list of OrderItems and each item has a list of Discounts
public class Order
{
public IList<OrderItem> OrderItems = new List<OrderItem>();
}
public class OrderItem
{
public IList<Discount> Discounts = new List<Discount>();
}
public class Discount
{
public string Desc { get; set; }
public decimal Amount { get; set; }
public bool IsActive { get; set; }
}
If I want to get a list of all discounts with an IsActive flag of true, how would I do this in a Linq query?
Currently this is what I have but it is very ugly with 2 foreach statements that need to be removed.
IList<Discount> activeDiscounts = new List<Discount>();
foreach (OrderItem item in order.OrderItems)
{
var aDiscounts = from discount in item.Discounts.AsEnumerable()
where discount.IsActive == true
select discount;
foreach (Discount discount in aDiscounts)
{
activeDiscounts.Add(discount);
}
}
Here's a one line linq statement:
order.OrderItems.SelectMany(item => item.Discounts.Where(discount => discount.IsActive));
I have these two classes:
public class Order
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOf { get; set; }
public int[] Product { get; set; }
public int[] Quantity { get; set; }
public int[] Storage { get; set; }
public override bool Equals(Order obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// Return true if the fields match:
return (ID == obj.ID);
}
}
public class RawOrderData
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOfID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
}
Every order in the system is in the form as class Order, the array is used when there are more than one product in the order.
RawOrderData is created from a JSON string where every product in the order have its own object. I want to create a List<Order> where every order gets its own object in the list so there not are several orders with same order id when order contains more than one product.
// raw data is here the JSON string
rawdatalist = serializer.Deserialize<List<RawOrderData>> (rawdata);
// Convert raw objects to List<Order>, list of orders
List<Order> orders = new List<Order> ();
orders = ConvertRawOrderToList (rawdatalist);
private List<Order> ConvertRawOrderToList(List<RawOrderData> datalist)
{
List<Order> orders = new List<Order> ();
foreach (RawOrderData dataobj in datalist)
{
// Check if order exists in list
if (orders.Contains(new Order () {ID = dataobj.ID}))
{
// Order exists, add more products
// CODE HERE?
} else {
// order not existing, add new order to list
short storage = GetStorageArea(dataobj.ProductID);
orders.Add (new Order () {ID = dataobj.ID, Output = dataobj.Output, Wharf = dataobj.Wharf, PartOf = dataobj.PartOfID, Product = dataobj.ProductID, Quantity = dataobj.Quantity});
}
}
return orders;
}
Do I think correct with the ConvertRawOrderToList method? The problem is I don't know what to write in // CODE HERE?. When there is array inside the list-object I'm confused.
I'm also wondering how to access all values in the List<Order> orders.
The information to Storage[] is created from another method that have product ID as input.
It sounds like you have a "flattened" collection of objects that you want to group into Orders. If that's the case, a basic Linq projection would be simplest:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
})
.ToList();
Then you don't need to worry about overriding Equals (at least not for this purpose).
Where would I add the method for adding Storage also?
Since your GetStorageArea function takes a single ProductID you need to pass the product IDs to that function:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
Storage = g.Select(o => GetStorageArea(o.Product)).ToArray()
})
.ToList();
public class Price
{
public int Id { get; set; }
public decimal Price { get; set; }
}
public class Product
{
public Product()
{
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}
I've searched in the internet the best way to set a default value in a field. They say put in the constructor or make a backing field. Now what if I want to set a default value in an entity from another entity's field value? Say, the default value of Product's Price is the latest Price(in the Price class)
How do you achieve that?
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)?
Any help would be much appreciated. Thanks
What i would do is when i query the DB to return the Product / Products, i would let it fill the price for me.
This is a just for demonstration purposes
I would do something like:
public class MyDataAccessLayer
{
public IEnumerable<Product> GetProducts()
{
return DbContext.Products.Select(x => new Product
{
Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id)
};
}
public Product GetProduct(string id)
{
var product = DbContext.Products.FirstOrDefault(x => x.Id == id);
if (product != null)
{
product.Price = Context.Price.FirstOrDefault().OrderByDescending(c => c.Id);
}
return product;
}
}
I would definitely let me Data Access Layer do the fetching for me and have it fill the latest price from the DB, and not have the POCO access my database.
Keep last inserted Producte in Cache. while you are creating new Product get last Product from Cache and set default values inside constructor and when you are inserting new Product update Cache.
While you are inserting new Product it's same as getting last Product from database because new inserted product is last product in database. So instead of:
var lastProduct = Context.Products.FirstOrDefault().OrderByDescending(c => c.Id);
do this:
var lastProduct = Context.Products.Add(newProduct);
Cache.Set("LastProduct", lastProduct, new CacheItemPolicy { SlidingExpiration = new TimeSpan(1, 0, 0, 0)});
There are some advantages here. first you dot query database every time for getting last product. second your are not going access EF Context inside your Poco class.
public class Product
{
public Product()
{
// var lastProduct = (Product)Cache["LastProduct"];
// Set default value here
}
public int Id { get; set; }
public string Sku { get; set; }
public int PriceId { get; private set; }
public virtual Price Price { get; set; }
}
2 classes
public class Student
{
public int StudentID { get; set;}
public string Name { get; set;}
public List<Fee> Fees {get;set;}
}
public class Fee
{
public int FeeID { get; set;}
public decimal FeeAmount { get; set; }
}
let say there are 10 students objects Student[] stud = new Student[10]
if stud[0] has 2 fees ( Fee[2] ) and they are
FeeID=1, FeeAmount=54.23
FeeID=2, FeeAmount=234.98
if stud[1] has 1 fees ( Fee[2] ) and they are
FeeID=1, FeeAmount=9.99
if stud[2] has 3 fees ( Fee[3] ) and they are
FeeID=1, FeeAmount=123.45
FeeID=2, FeeAmount=67.89
FeeID=3, FeeAmount=987.65
I need to sort the Student Collections by TotalAmount(Fee Collection)
TotalAmount of Fee
stud[0] = 54.23+234.98=289.21
stud[1] = =9,99
stud[2] = 123.45+67.89+987.65=1178.99
there for after sorted it should become
stud[0] = 123.45+67.89+987.65=1178.99
stud[1] = 54.23+234.98=289.21
stud[2] = =9,99
It sounds like you just want:
stud = stud.OrderByDescending(x => x.Fees.Sum(fee => fee.FeeAmount)).ToArray();
Gotta love LINQ :)
A couple of things to note:
This will still only calculate the sum of the fees once per student
This will not currently handle null elements. Do you need to? (You seem to have a fixed array size... perhaps use List<Student> instead?)
Unless you actually need it as an array afterwards, just drop the ToArray call. Be aware that it will sort it every time you iterate through it unless you use ToArray or ToList though.
var results = stud.OrderByDescending(s => s.Fees.Sum(f => f.FeeAmount)).ToArray();
A simple Linq query should do the job:
stud =
(from s in stud
orderby s.Fees.Sum(f => f.FeeAmount)
select s)
.ToArray();
var students = new List<Student>() { .. add students here ... };
students.OrderBy(x => x.Fees.Sum(y => y.FeeAmount));
And if you use an old .net framework (without Linq) :
public class Student : IComparable
{
public int StudentID { get; set; }
public string Name { get; set; }
public List<Fee> Fees { get; set; }
private decimal TotalAmount
{
get
{
decimal total = 0;
if (Fees != null)
foreach (var fee in Fees)
total += fee.FeeAmount;
return total;
}
}
public int CompareTo(object obj)
{
//Ascending
//return TotalAmount.CompareTo((obj as Student).TotalAmount);
//Descending
return (obj as Student).TotalAmount.CompareTo(TotalAmount);
}
}
public class Fee
{
public int FeeID { get; set; }
public decimal FeeAmount { get; set; }
}
List<Student> students = new List<Student>();
...
students.Sort();
Linq is better...