Reputation: 662
I have two lists:
var qtys = new List<InventoryQuantity>()
{
new InventoryQuantity() { WarehouseId = 1, QuantityInWarehouse = 0 },
new InventoryQuantity() { WarehouseId = 2, QuantityInWarehouse = 452 },
new InventoryQuantity() { WarehouseId = 3, QuantityInWarehouse = 184 },
new InventoryQuantity() { WarehouseId = 4, QuantityInWarehouse = 328 },
new InventoryQuantity() { WarehouseId = 5, QuantityInWarehouse = 0 },
};
var times = new List<WarehouseTransitTime>()
{
new WarehouseTransitTime() { WarehouseId = 1, TransitDays = 1 },
new WarehouseTransitTime() { WarehouseId = 2, TransitDays = 4 },
new WarehouseTransitTime() { WarehouseId = 3, TransitDays = 2 },
new WarehouseTransitTime() { WarehouseId = 4, TransitDays = 3 },
new WarehouseTransitTime() { WarehouseId = 5, TransitDays = 5 },
};
class InventoryQuantity
{
public int WarehouseId { get; set; }
public int QuantityInWarehouse { get; set; }
}
class WarehouseTransitTime
{
public int WarehouseId { get; set; }
public int TransitDays { get; set; }
}
I need to return the WarehouseId from qtys where the Quantity > 0 and the WarehouseId equals the minimum transit days WarehouseId in times.
I know I can do something like below but seems clunky and there must be an elegant solution.
public int NearestWarehouse()
{
var withQty = qtys.Where(i => i.QuantityInWarehouse > 0);
var orderedTransit = times.OrderBy(tt => tt.TransitDays).ToList();
//loop and compare
}
Example data:
qtys
WarehouseId | Quantity
1 | 0
2 | 452
3 | 184
4 | 328
5 | 0
times
WarehouseId | TransitTime
1 | 1
2 | 4
3 | 2
4 | 3
5 | 5
Expected output would be 3, because warehouse 3 has inventory and the shortest transit time (2)
Upvotes: 0
Views: 116
Reputation: 1882
You can do it like this..
var withQty = (from q in qtys
join t in times on q.WarehouseId equals t.WarehouseId
where q.QuantityInWarehouse > 0
select new { q.WarehouseId, t.TransitDays })
.OrderBy(item => item.TransitDays).FirstOrDefault();
return withQty?.WarehouseId ?? 0;
Upvotes: 1
Reputation: 117057
It seems to me that the cleanest and simplest query is this:
var query =
from q in qtys
where q.QuantityInWarehouse > 0
join t in times on q.WarehouseId equals t.WarehouseId
orderby t.TransitDays
select q.WarehouseId;
var warehouseId = query.FirstOrDefault();
This gives me 3
.
Upvotes: 2
Reputation: 3039
What you want is a group join:
Functional Syntax
var query1 = qtys.Where(q => q.QuantityInWarehouse > 0)
.GroupJoin(times, q => q.WarehouseId, t => t.WarehouseId, (q, t) => new { q.WarehouseId, TransitDays = t.DefaultIfEmpty().Min(grp => grp?.TransitDays) })
.OrderBy(g => g.TransitDays)
.FirstOrDefault();
Query Syntax
var query2 = from q in qtys
join t in times on q.WarehouseId equals t.WarehouseId into grp
where q.QuantityInWarehouse > 0
select new
{
q.WarehouseId,
TransitDays = grp.DefaultIfEmpty().Min(g => g?.TransitDays)
};
var result = query2.OrderBy(g => g.TransitDays)
.FirstOrDefault();
A group join will join two lists together on their corresponding keys--similar to a database join--and the associated values to those keys will be grouped into an enumerable. From that enumerable, you can derive the minimum value that you care about, TransitDays
in this case.
There is no equivalent to "first or default" in query syntax. The easiest approach is just to apply the same OrderBy
and FirstOrDefault
against the query variable, as demonstrated above.
Upvotes: 2
Reputation: 529
Well you mention an AND relation between the two, right?
I was thinking of databases with a forignkey... but Linq prety much does it if your lists aren't to big:
keys = qtys.Where(i => i.QuantityInWarehouse > 0).Select(i => i.WarehouseId).ToList();
// get the smallest not a list
var result = times.Where(tt => keys.Contains(tt.wharehouseId)).orderby(tt => tt.Transitdays).FirstOrDefault();
Otherwise you could have Dictionary
with ID as key...
Upvotes: 1