How to convert rows into columns using LINQ? - linq

How do I translate all rows into columns? Considering first column will generate as columns name using LINQ.
Calculate sum if product has duplicate like product1:
I have a list contains below data
Product Y1 Y2 Y3 Y4 Y5 Y6 Y7 Y8 Y9 Y10
Product1 1 2 3 4 5 6 7 8 9 10
Product1 2 3 4 5 6 7 8 9 10 11
Product2 2 3 4 5 6 7 8 9 10 11
Product3 3 4 5 6 7 8 9 10 11 12
Product4 4 5 6 7 8 9 10 11 12 13
I need to get below output as a list
YKey Year Product1 Product2 Product3
Y1 2016 3 2 3 4
Y2 2017 5 3 4 5
Y3 2018 7 4 5 6
Y4 2019 9 5 6 7
Y5 2020 11 6 7 8
Y6 2021 13 7 8 9
Y7 2022 15 8 9 10
Y8 2023 17 9 10 11
Y9 2024 19 10 11 12
Y10 2025 21 11 12 3
I have just 4 products just to give you some clear picture about my requirement, I may have many product value for each year wise for fixed 20 years.
Year will start from current year, if current year 2016 then y1=2016,y2=2017 so on..
Below I have provided a list<class> as a example
private List<Data> CreateColumnData()
{
var list = new List<Data>();
list.Add(new Data() { ProductName = "Product1", Year1 = 1, Year2 = 2, Year3 = 3, Year4 = 4, Year5 = 5, Year6 = 6, Year7 = 7,Year8 = 8,Year9=9,Year10=10});
list.Add(new Data() { ProductName = "Product2", Year1 = 2, Year2 = 3, Year3 = 4, Year4 = 5, Year5 = 6, Year6 = 7, Year7 = 8, Year8 = 9, Year9 = 10, Year10 = 11 });
list.Add(new Data() { ProductName = "Product3", Year1 = 3, Year2 = 4, Year3 = 5, Year4 = 6, Year5 = 7, Year6 = 8, Year7 = 9, Year8 = 10, Year9 = 11, Year10 = 12 });
list.Add(new Data() { ProductName = "Product4", Year1 = 4, Year2 = 5, Year3 = 6, Year4 = 7, Year5 = 8, Year6 = 9, Year7 = 10, Year8 = 11, Year9 = 12, Year10 = 13});
return list;
}
public class Data
{
public string ProductName { get; set; }
public int Year1 { get; set; }
public int Year2 { get; set; }
public int Year3 { get; set; }
public int Year4 { get; set; }
public int Year5 { get; set; }
public int Year6 { get; set; }
public int Year7 { get; set; }
public int Year8 { get; set; }
public int Year9 { get; set; }
public int Year10 { get; set; }
public int Year11 { get; set; }
public int Year12 { get; set; }
public int Year13 { get; set; }
public int Year14 { get; set; }
public int Year15 { get; set; }
}

I see not so much work for Linq, just FirstOrDefault.
class Program
{
static void Main()
{
List<Data> src = CreateColumnData();
// Result is a List
List<ResultRow> res = new List<ResultRow>();
foreach (Data dd in src) {
for (int i = 1; i <= 15; i++)
{
String yk = "Y" + i.ToString();
ResultRow r = res.FirstOrDefault(rr => rr.YK == yk);
if (r == null)
{
r = new ResultRow(yk, 2015 + i, 0, 0, 0, 0);
res.Add(r);
}
switch (dd.ProductName)
{
case "Product1": r.Product1 += dd.getYear(i); break;
case "Product2": r.Product2 += dd.getYear(i); break;
case "Product3": r.Product3 += dd.getYear(i); break;
case "Product4": r.Product4 += dd.getYear(i); break;
default: break;
}
}
}
res.All(rr => {
Console.WriteLine(rr.ToString());
return true; });
Console.ReadKey();
}
static private List<Data> CreateColumnData()
{
var list = new List<Data>();
list.Add(new Data() { ProductName = "Product1", Year1 = 1, Year2 = 2, Year3 = 3, Year4 = 4, Year5 = 5, Year6 = 6, Year7 = 7, Year8 = 8, Year9 = 9, Year10 = 10 });
list.Add(new Data() { ProductName = "Product2", Year1 = 2, Year2 = 3, Year3 = 4, Year4 = 5, Year5 = 6, Year6 = 7, Year7 = 8, Year8 = 9, Year9 = 10, Year10 = 11 });
list.Add(new Data() { ProductName = "Product3", Year1 = 3, Year2 = 4, Year3 = 5, Year4 = 6, Year5 = 7, Year6 = 8, Year7 = 9, Year8 = 10, Year9 = 11, Year10 = 12 });
list.Add(new Data() { ProductName = "Product4", Year1 = 4, Year2 = 5, Year3 = 6, Year4 = 7, Year5 = 8, Year6 = 9, Year7 = 10, Year8 = 11, Year9 = 12, Year10 = 13 });
return list;
}
}
// result POCO
public class ResultRow
{
public string YK { get; set; }
public int Year { get; set; }
public int Product1 { get; set; }
public int Product2 { get; set; }
public int Product3 { get; set; }
public int Product4 { get; set; }
public ResultRow (string YK, int Year, int Product1, int Product2, int Product3, int Product4)
{
this.YK = YK;
this.Year = Year;
this.Product1 = Product1;
this.Product2 = Product2;
this.Product3 = Product3;
this.Product4 = Product4;
}
public override string ToString() {
return string.Format("{0},{1},{2},{3},{4},{5}",
YK, Year, Product1, Product2, Product3, Product4);
}
}
public class Data
{
public string ProductName { get; set; }
public int Year1 { get; set; }
public int Year2 { get; set; }
public int Year3 { get; set; }
public int Year4 { get; set; }
public int Year5 { get; set; }
public int Year6 { get; set; }
public int Year7 { get; set; }
public int Year8 { get; set; }
public int Year9 { get; set; }
public int Year10 { get; set; }
public int Year11 { get; set; }
public int Year12 { get; set; }
public int Year13 { get; set; }
public int Year14 { get; set; }
public int Year15 { get; set; }
public int getYear(int i) {
switch (i) {
case 1: return Year1;
case 2: return Year2;
case 3: return Year3;
case 4: return Year4;
case 5: return Year5;
case 6: return Year6;
case 7: return Year7;
case 8: return Year8;
case 9: return Year9;
case 10: return Year10;
case 11: return Year11;
case 12: return Year12;
case 13: return Year13;
case 14: return Year14;
case 15: return Year15;
default: return 0;
}
}
}

Related

LINQ: Traverse up a hierarchy to retrieve parent hierarchy

I have some data which represents parent-child relationship on the same entity. Given a node, I need to find its entire upper hierarchy (parent, grand-parent, great grand-parent, etc..)
My entity is like this:
public partial class Location{
public int LocationId { get; set; }
public int? FkParentLocationId { get; set; }
..... more properties here.......
public virtual Location FkParentLocation { get; set; }
public virtual ICollection<Location> InverseFkParentLocation { get; set; }
}
I'm referring to the Hierarchy traverse implementation suggested here, but it works when you go down the hierarchy. How would retrieve the upper hierarchy using LINQ?
Sample data:
List<Location> locations = new List<Location> {
new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" } };
Expected output: given location Id:5, I should get a list containing the locations 3 and 2 (as they are the parents).
Here is an approach you could use, demoed with a console app. Heavily borrowing from Jon Skeet.
using System;
using System.Collections.Generic;
using System.Linq;
namespace Locations
{
public partial class Location
{
public int LocationId { get; set; }
public int? FkParentLocationId { get; set; }
public virtual Location FkParentLocation { get; set; }
public virtual ICollection<Location> InverseFkParentLocation { get; set; }
public string LocationName { get; set; }
public string LocationDisplayName { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Location> locations = new List<Location> {
new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" } };
var result = GetAncestorsIds(locations, 5);
foreach (var id in result)
{
System.Console.WriteLine(id);
}
}
private static IEnumerable<int> GetAncestorsIds(List<Location> locations, int id)
{
Location location = locations.SingleOrDefault(l => l.LocationId == id);
if(location != null)
{
while(location != null && location.FkParentLocationId != null)
{
location = locations.SingleOrDefault(l => l.LocationId == location.FkParentLocationId);
if(location != null)
{
yield return location.LocationId;
}
}
}
}
}
}
And this approach can be turned into your own Linq extension. Here's how it could look.
public static class MyExtensions
{
public static IEnumerable<int> GetAncestorIds<TSource>(this IEnumerable<TSource> source, Func<TSource, int> pk, Func<TSource, int?> fk, int id)
{
TSource currentObj = source.SingleOrDefault(s => pk(s) == id);
while(currentObj != null && fk(currentObj) != null)
{
currentObj = source.SingleOrDefault(s => pk(s) == fk(currentObj));
if(currentObj != null)
{
yield return pk(currentObj);
}
}
}
}
and then to call this for your scenario you would do this
var result = locations.GetAncestorIds(l => l.LocationId, l => l.FkParentLocationId, 5);

Summing in multi-level relationship

Using EF code first, I have the following 4 entities
public class Item {
public int Id { get; set; }
public string Name { get; set; }
}
public class Location {
public int Id { get; set; }
public string Name { get; set; }
}
public class InventoryAdjustment {
public int Id { get; set; }
public virtual Location Location { get; set; }
public virtual ICollection<AdjustmentLine> Lines { get; set; }
}
public class AdjustmentLine {
public int Id { get; set; }
public virtual Item Item { get; set; }
public int Quantity { get; set; }
}
What I am trying to do is to get the sum of all inventory adjustments for each item at each location using minimal database round-trips.
The best I achieved so far is:
using (var db = new InventoryContext()) {
var items = db.Items.ToList();
var locations = db.Locations.ToList();
foreach (var item in items) {
Console.WriteLine(item.Name+":");
foreach (var location in locations) {
Console.Write("\t" + location.Name + ": ");
var qty = db.InventoryAdjustments
.Where(p => p.Location.Id == location.Id)
.SelectMany(p => p.Lines)
.Where(p => p.Item.Id == item.Id)
.Select(p => (int?)p.Quantity)
.Sum();
Console.WriteLine(qty ?? 0);
}
}
Console.Read();
}
The above outputs:
Item1:
Location1: 2
Location2: 12
Location3: 21
Item2:
Location1: 4
Location2: 0
Location3: 0
Item3:
Location1: 1
Location2: 17
Location3: 0
But with 3 items and 3 locations in the database, the above code causes 11 calls to the database. 2 for getting items and locations, and 9 for calculating the sum of quantity.
Is there a better way to get the sum with the least amount of round-trips?
This should probably work:
using (var db = new InventoryContext())
{
var items = db.Items.ToList();
var locations = db.Locations.ToList();
items
.Select(item =>
{
Console.WriteLine(item.Name + ":");
return item;
})
.SelectMany(item => locations.Select(location => new { item, location }))
.GroupJoin(
db.InventoryAdjustments
.SelectMany(
inventoryAdjustment => inventoryAdjustment.Lines.Select(
adjustmentLine => new { key = new { locationId = inventoryAdjustment.Location.Id, itemId = adjustmentLine.Item.Id }, adjustmentLine.Quantity }
)
),
x => new { locationId = x.location.Id, itemId = x.item.Id },
y => y.key,
(x, y) =>
{
Console.WriteLine("\t {0}: {1}", x.location.Name, y.Sum(a => a.Quantity));
return 0;
}
).ToList();
Console.Write("\nPress any key...");
Console.ReadKey();
}

select and update based on a child objects property minimum value using Linq

I have a Type Supplier that has a property SupplierId and a another property NearestLocation which is a of Type SupplierLocation, the SupplierLocation consists of properties SupplierId and DistanceFromDevice
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation { get; set; }
}
class SupplierLocation
{
public int SupplierId { get; set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
I have a List of all my Supplierlocations a supplier can have a n number of locations. I have also calculated the DistanceFromDevice property for each location.
I have a List whose id's can be found in the SupplierLocations List.
What I would like to do using linq is to join my supplier to the SupplierLocation by the SupplierId and populate the NearestLocation Property of the Supplier class with the the Location that has the least DistanceFromDevice value of all the locations for that particular supplier.
Hope this makes sense. Can this be done using linq.
Many thanks in advance.
Paul
Here is a working example in LINQPad
void Main()
{
var suppliers = new List<Supplier>
{
new Supplier() {SupplierId = 1},
new Supplier() {SupplierId = 2},
new Supplier() {SupplierId = 5}
};
var locations = new List<SupplierLocation>
{
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 10, Latitude = 1, Longitude = 2},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 20, Latitude = 1, Longitude = 3},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 30, Latitude = 1, Longitude = 4},
new SupplierLocation {SupplierId = 1, DistanceFromDevice = 40, Latitude = 1, Longitude = 5},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 10, Latitude = 2, Longitude = 2},
new SupplierLocation {SupplierId = 2, DistanceFromDevice = 20, Latitude = 2, Longitude = 3},
new SupplierLocation {SupplierId = 3, DistanceFromDevice = 10, Latitude = 3, Longitude = 2}
};
var result = from s in suppliers
join l in locations on s.SupplierId equals l.SupplierId
into grp
where grp.Count() > 0
select new Supplier() { SupplierId = s.SupplierId, NearestLocation = grp.OrderBy (g => g.DistanceFromDevice).First()};
result.Dump();
}
class Supplier
{
public int SupplierId { get; set; }
public SupplierLocation NearestLocation{ get; set; }
}
class SupplierLocation
{
public int SupplierId { get ;set; }
public decimal DistanceFromDevice { get; set; }
public double Longitude { get; set; }
public double Latitude {get; set;}
}
So, you want to set NearestLocation on Supplier where the SupplierId is equal to one in List<SupplierLocation>?
Assume you have a List<SupplierLocation> named "Locations" and "currentSupplier" is the Supplier you want to assign the NearestLocation of:
try
{
var minForSupplier = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId).Min(x => x.DistanceFromDevice);
currentSupplier.NearestLocation = Locations.Where(x => x.SupplierId == currentSupplier.SupplierId && x.DistanceFromDevice == minForSupplier).Single();
}
catch(InvalidOperationException e)
{
// There is more than one SupplierLocation
}

LINQ grouping by and creating a formula based on the returned results

I need some help adding conditions to a linq query.
Take this small example
static void Main(string[] args)
{
List<ItemEntry> items = new List<ItemEntry>();
items.Add(new ItemEntry(1,1,5,1));
items.Add(new ItemEntry(2, 1, 5, 1));
items.Add(new ItemEntry(3, 1, 5, 1));
items.Add(new ItemEntry(4, 1, 10, 3));
items.Add(new ItemEntry(5, 2, 5, 1));
items.Add(new ItemEntry(6, 2, 5, 1));
items.Add(new ItemEntry(7, 2, 5, 1));
items.Add(new ItemEntry(8, 2, 5, 1));
items.Add(new ItemEntry(9, 2, 5, 2));
items.Add(new ItemEntry(10, 2, 5, 2));
var results = from data in items
group data by data.ItemTypeID
into grouped
select new { ItemID = grouped.Key, ItemPrice = grouped.Sum(data => data.ItemPrice) };
foreach (var item in results)
{
Console.WriteLine(item.ItemID + " " + item.ItemPrice);
}
Console.ReadLine();
}
}
public class ItemEntry
{
public int ItemID { get; set; }
public int ItemTypeID { get; set; }
public double ItemPrice { get; set; }
public int ProcessedType { get; set; }
public ItemEntry(int itemID, int itemTypeID,double itemPrice, int processedType)
{
ItemID = itemID;
ItemTypeID = itemTypeID;
ItemPrice = itemPrice;
ProcessedType = processedType;
}
}
I am grouping ItemTypeID then creating a sum based on the group which is fine. I now need to add an extra 2 conditions in.
I want to keep the grouping the same but I want to do the same only if ProcessedType ==1 if its types 2 or 3 then I want to total them and then minus it from the grouped total.
e.g. I will use ItemTypeID 1
If I total all of ProcessedType 1 I get 15 and if I total ProcessedType3 I have 10 so I want to return 5.
I am not sure how to add the condition into my code.
Any help would be great!!!
TIA
Add a new property to your class that does the work for you.
public class ItemEntry
{
// keep the existing properties
...
public int TotalModifier
{
get
{
if (this.ProcessedType == 1)
return this.ItemPrice;
else
return -(this.ItemPrice);
}
}
Now sum that new property
var results = from data in items
group data by data.ItemTypeID
into grouped
select new { ItemID = grouped.Key, ItemPrice = grouped.Sum(data => data.TotalModifier) };
You'll want to give this property a better name than I have, but the logic is there for you.

Linq to Objects - Where search within a list

Linq to Objects - Where search within a list
internal class ProdQtyByWarehouse
{
public int id { get; set; }
public List<ProdWarehouseQty> ProdWarehouseQtys { get; set; }
}
internal class ProdWarehouseQty
{
public int id { get; set; }
public string PName { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
var list1 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 3,
PName = "list1PN1"
},
new ProdWarehouseQty
{
id = 4,
PName = "list1PN2"
}
};
var list2 = new List<ProdWarehouseQty>
{
new ProdWarehouseQty
{
id = 5,
PName = "list2PN1"
},
new ProdWarehouseQty
{
id = 6,
PName = "list2PN2"
}
};
var prodQtyByWarehouses = new List<ProdQtyByWarehouse>
{
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list1},
new ProdQtyByWarehouse {id = 1, ProdWarehouseQtys = list2}
};
List<int> integers = new List<int>{2,3,4,6};
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id = 3})
select c).ToList(); // no object is returned
}
How can i achieve:
List<ProdQtyByWarehouse> list =
(from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Contains(new ProdWarehouseQty {id in integers})
select c).ToList();
List<ProdQtyByWarehouse> list =
(
from c in prodQtyByWarehouses
where c.ProdWarehouseQtys.Exists(x => integers.Contains(x.id))
select c
).ToList();

Resources