Array inside list object, how to handle? - c#

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();

Related

Linq method - Return all Recipes that contain all Tags selected

This query gets all the rows from the join table, TagRecipes (many-to-many), where the TagId is found in a list, tagIdList, and lastly just returns the Recipe. How can I make it so it only returns Recipes that have all the tags in the list, tagIdList?
Basically, it's filtering by 'or', but I want it to filter by 'and'. The recipe must contain ALL the tags, not just some.
var allRecipes = await _context.TagRecipes
.Where(tr => tagIdList.Contains(tr.TagId))
.Select(i => i.Recipe).Distinct()
.ToListAsync();
e.g, tagIdList = {17, 20 ,21 }
TagRecipes
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains TagId 17
Classes
public class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public string Ingredients { get; set; }
public string Instructions { get; set; }
public string ImageURL { get; set; }
public string Description { get; set; }
public ICollection<TagRecipe> TagRecipes { get; set; }
public virtual ICollection<StarRating> StarRatings { get; set; }
public ICollection<Binder> Binders { get; set; }
}
public class TagRecipe
{
public int TagId { get; set; }
public int RecipeId { get; set; }
public Tag Tag { get; set; }
public Recipe Recipe { get; set; }
}
Thank you
group TagRecipes by RecipId, so each RecipId (Key) has its own tagIds.
loop on each group to check if it has all the tags in the provided tagIdList, and if it has them all, store this RecipId (Key), in my case i created list of int RecipIds.
get all the Recipes in the RecipIds list.
I hope this could be helpful
List<int> RecipIds = new List<int>();
int count = 0;
var RecipGroup = _context.TagRecipes.GroupBy(tr => tr.RecipeId);
foreach (var group in RecipGroup)
{
count = 0;
foreach (var tr in group)
{
if (tagIdList.Contains(tr.TagId))
{
count += 1;
}
}
if (tagIdList.Length == count)
{
RecipIds.Add(group.Key);
}
}
var allRecipes = _context.Recipes.Where(r => RecipIds.Contains(r.Id)).ToList();
I believe the following solution using Linqkit will be the simplest way to solve this, and without returning duplicates.
var tagIdList = new List<int> {1, 2};
var predicate = tagIdList.Aggregate(PredicateBuilder.New<Recipe>(), (pred, currentTagId) =>
pred.And(recipe => recipe.TagRecipes.Any(x => x.TagId == currentTagId)));
var result = _context.Recipes.Where(predicate).ToList();
Generates this SQL:
SELECT "r"."Id", "r"."Name"
FROM "Recipes" AS "r"
WHERE EXISTS (
SELECT 1
FROM "TagRecipes" AS "t"
WHERE ("r"."Id" = "t"."RecipeId") AND ("t"."TagId" = #__currentTagId_0)) AND EXISTS (
SELECT 1
FROM "TagRecipes" AS "t0"
WHERE ("r"."Id" = "t0"."RecipeId") AND ("t0"."TagId" = #__currentTagId_1))
Code is tested and verified using an asp.net Core 5 app.
Basically, it's filtering by 'or', but I want it to filter by 'and'.
The recipe must contain ALL the tags, not just some.
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains
TagId 17
Since you want to filter the data by and, after filtering the data based on the tag, if group the result based on the RecipeId property, the item count in the group should be the equally with the tag list count. So, you could use the following query statement:
var tagIdList = new List<int>() { 17, 20, 21 };
var allRecipes = _context.TagRecipes.Include(tr => tr.Recipe)
.Where(tr => tagIdList.Contains(tr.TagId))
.ToList()
.GroupBy(tr=> tr.RecipeId)
.Where(c => c.Count() == tagIdList.Count)
.Select(i => new { RecipeId = i.Key, Count = i.Count(), Recipe = i.FirstOrDefault().Recipe })
.ToList();
The result as below:

c# LINQ query to select whole object into new?

Not sure if i worded the question correctly, but what im trying to do is return a new viewmodel with one of the parts being a booking:
public class Booking
{
public int BookingId { get; set; }
public int CustomerId { get; set; }
public Guid UniqueId { get; set; }
public string EventId { get; set; }
public bool IsPaid { get; set; }
public double Price { get; set; }
public DateTime BookingDate { get; set; }
public DateTime DateBooked { get; set; }
[JsonIgnore]
public Customer Customer { get; set; }
[JsonIgnore]
public ICollection<BookingService> BookingServices { get; set; }
[NotMapped]
public IEnumerable<Service> Services { get; set; }
}
and my query is:
var customers = _dbContext.Customers
.Select(c => new CustomerBookingsViewModel
{
Customer = c,
Bookings = c.Bookings.Select(b => new Booking
{
BookingId = b.BookingId,
BookingDate = b.BookingDate,
DateBooked = b.DateBooked,
CustomerId = b.CustomerId,
UniqueId = b.UniqueId,
EventId = b.EventId,
IsPaid = b.IsPaid,
Price = b.Price,
Services = b.BookingServices.Select(s => s.Service)
}),
}
)
.ToList();
What I want to know is how to I select all the booking info into the booking without selecting each part, ie:
BookingId = b.BookingId,
BookingDate = b.BookingDate,
DateBooked = b.DateBooked,
CustomerId = b.CustomerId,
UniqueId = b.UniqueId,
EventId = b.EventId,
IsPaid = b.IsPaid,
Price = b.Price,
Can it be done or because the list of services is inside the booking model it cant?
Thanks.
You could implement the IClonable interface on your class.
public class MyClass : ICloneable
{
public int Id { get; set; }
public object Clone() => MemberwiseClone();
}
Usage:
var list1 = new List<MyClass>
{
new MyClass() { Id = 2 },
new MyClass() { Id = 5 }
};
var list2 = list1.Select(x => (MyClass)x.Clone()).ToList();
list2.First().Id = 10; //list1 won't be affected
You should use AutoMapper here to avoid writing each path.
https://automapper.org/
http://docs.automapper.org/en/stable/Getting-started.html
There is no other way, at least it is not related to LINQ or queries.
The question "How to clone an object" has been answered here:
Creating a copy of an object in C#
There is no LINQ way to do this. I would suggest using custom Attribute marking every property you want to copy. This would help if you want not to copy the whole object but some properties. After marking every property you need you can just set the marked props with reflection from one of the objects to the other.

Linq filtering nested collection with keeping the parent entity

I would like to get clients and their orders with cost > 100, which means I want to access client.Orders and fetch only those that have cost > 100.
var list1 = list.SelectMany(c => c.Orders.Where(x => x.Cost > 100), (c,x)=> c).Distinct();
public class Client
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public bool ClubMember { get; set; }
public List<Order> Orders { get; set; }
}
public class Order
{
public int ItemCount { get; set; }
public int Cost { get; set; }
}
This returns all the clients who have A order with cost > 100, I want the orders to be filtered out and skip all the ones that do not match my condition. I tried it in some other ways, but did not succeed :( An explanation on the method would be fantastic to have as well.
I assume that you don't want to modify the content of your Client objects, here is my proposal:
var list2 = list.Select(c => new // 1
{
client = c,
orders = c.Orders.Where(x => x.Cost > 100)
})
.Where(a => a.orders.Any()) //2
.ToList();
1- Select a new entity which contains (for every initial client) a reference to the client along with a filtered list of its Orders
2- Keep only objects where there is at least one Order (already filtered)
Maybe so:
var clientsWithOnlyOneCost100 = clients.Select(c => new
{
client = c,
orders = c.Orders.Where(o => o.Cost > 100)
})
.Where(x => x.orders.Count() == 1);

Add data to a list insde a list where item in the prerent list

I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.

searching a List<>

I have a List<Order>
public int OrderID { get; set; }
public string CustID { get; set; }
public string Details { get; set; }
I want to write a method that accepts a ID, then searches this List for matching records that have same CustID and returns ORderID and Details in a List<>
This will get a sequence of Order objects that match the criteria:
var ordersIWant = myList.Where(order => order.CustID == "some customer ID");
public List<Order> Get(string id)
{
List<Order> orders = new List<Order>(); // pass this in as a param or globally refer to it
var query = from o in orders
where o.CustID == id
select o;
return query.ToList();
}
Or if you want to specifically return only those two fields maybe something like:
public class Order : IOrderDetails
{
public int OrderID { get; set; }
public string CustID { get; set; }
public string Details { get; set; }
}
public interface IOrderDetails
{
int OrderID { get; set; }
string Details { get; set; }
}
public List<IOrderDetails> Get(string id)
{
List<Order> orders = new List<Order>(); // pass this in as a param or globally refer to it
var query = from o in orders
where o.CustID == id
select o as IOrderDetails;
return query.ToList();
}
Assuming those properties you listed belong to a class.
string searchId="15";
var list = (from item in myList
where item.OrderId == searchId
select new {OrderId= item.OrderId,Details = item.Details }).ToList();
Just wrote that without compiling... good luck.
Since you only wanted OrderID and Details I returned an anonymous object. Could also just return item.

Categories

Resources