using Linq to break flat table results into object collection - c#

I have a database return result which has flatten results like below. I want to use Linq to break the flat results into primary classes with the items populating the primary class items property collection.
public class Result
{
public string PrimaryKey { get; set; }
public string Status { get; set; }
public string ItemName { get; set; }
}
public class ObjectA
{
public string PrimaryKey { get; set; }
public string Status { get; set; }
public List<Item> Items = new List<Item>();
}
public class Item
{
public string Name { get; set; }
}
static void Main(string[] args)
{
GetObjectAs();
}
static List<ObjectA> GetObjectAs()
{
// this is our table results
List<Result> results = new List<Result>();
results.Add(new Result()
{
PrimaryKey = "1",
Status = "Done",
ItemName = "item1"
});
results.Add(new Result()
{
PrimaryKey = "2",
Status = "Fail",
ItemName = null
});
results.Add(new Result()
{
PrimaryKey = "3",
Status = "Done",
ItemName = "item2"
});
results.Add(new Result()
{
PrimaryKey = "3",
Status = "Done",
ItemName = "item3"
});
List<ObjectA> returnResults = new List<ObjectA>();
// need to break into 3 ObjectA objects
// ObjectA 1 needs an Item added to its Items collection with ItemName item1
// ObjectA 2 has no items since the ItemName above is null
// ObjectA 3 needs 2 Items added to its Items collection item2 and item3
// return our collection
return returnResults;
}
PS this is just sample code, I know you shouldn't expose a List as a public property and should return an IEnumerator instead of the actual List etc.

You can use GroupBy to group the results by the primary key, then you can operate on the subset of rows within the group to obtain the status (hopefully all values for Status are the same, which is why I used First) and the list of items.
var items = results.GroupBy(r => r.PrimaryKey).Select(grp => new ObjectA()
{
PrimaryKey = grp.Key,
Status = grp.Select(r => r.Status).First(),
Items = grp.Where(r => r.ItemName != null)
.Select(r => new Item() { Name = r.ItemName }).ToList()
}).ToList();

return results
.GroupBy(r => r.PrimaryKey)
.Select(grp => new ObjectA
{
PrimaryKey = grp.Key,
Status = grp.First().Status,
Items = grp.Where(i => i.ItemName != null).Select(i => new Item { Name = i.ItemName }).ToList()
}).ToList();

Related

How do I specify a condition to copy values from one array to a sub array [duplicate]

I have the following business objects:
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
List<ItemCategoryBO> categoryList = new List<ItemCategoryBO>();
ItemCategoryBO itemCategory = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "CARS";
itemCategory.Title = "Cars";
ItemCategoryBO itemCategory2 = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "PLANES";
itemCategory.Title = "Planes";
categoryList.Add(itemCategory);
categoryList.Add(itemCategory2);
List<ItemBO> itemList = new List<ItemBO>();
ItemBO item1 = new ItemBO();
item1.ItemId = 1;
item1.Title = "1st item";
item1.ItemCategoryCd = "OTHER";
ItemBO item2 = new ItemBO();
item2.ItemId = 2;
item2.Title = "2nd Item";
item2.ItemCategoryCd = "CARS";
ItemBO item3 = new ItemBO();
item3.ItemId = 3;
item3.Title = "3rd Item";
item3.ItemCategoryCd = "PLANES";
itemList.Add(item1);
itemList.Add(item2);
itemList.Add(item3);
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
If you have a situation like:
List<ItemBO> items;
List<ItemCategoryBO> categories;
and you wish to get all the items that have a category that is in your list of categories, you can use this:
IEnumerable<ItemBO> result = items.Where(item =>
categories.Any(category => category.ItemCategory.equals(item.ItemCategory)));
The Any() operator enumerates the source sequence and returns true as soon as an item satisfies the test given by the predicate. In this case, it returns true if the categories list contains an ItemCategoryBO where its ItemCategory string is the same as the item's ItemCategory string.
More information about it on MSDN
Try using some linq
List<ItemBO> itm = new List<ItemBO>;
//Fill itm with data
//get selected item from control
string selectedcategory = cboCatetories.SelectedItem;
var itms = from BO in itm where itm.ItemCategory = selectedcategory select itm;
itms now contains all items in that category
Here's something I did in Linqpad
void Main()
{
var cat1 = new ItemCategoryBO {ItemCategory="c1", Title = "c1"};
var cat2 = new ItemCategoryBO {ItemCategory="c2", Title = "c2"};
var item1 = new ItemBO { ItemId = 1, Title = "item1", ItemCategory="c1"};
var item2 = new ItemBO { ItemId = 1, Title = "item2", ItemCategory="c2"};
var item3 = new ItemBO { ItemId = 1, Title = "item3", ItemCategory="c2"};
var item4 = new ItemBO { ItemId = 1, Title = "item4", ItemCategory="c3"};
var items = new List() {item1, item2, item3, item4};
var categories = new List() {cat1, cat2};
var itemsInCategory = from item in items
join category in categories on item.ItemCategory equals category.ItemCategory into itemInCategory
from categoryItem in itemInCategory
select new {item.Title, item.ItemCategory};
itemsInCategory.Dump();
}
// Define other methods and classes here
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
This returns:
Title, ItemCategory
item1 c1
item2 c2
item3 c2
Try this:
List<ItemBO> items = ...;
ItemCategoryBO category = ...;
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Updated to address OP's updated question:
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
I think you actually should do this in two steps. First, get your distinct list of items. Then, from your items, get your list of categories. So:
// First, get the distinct list of items
List<ItemBO> items = new List<ItemBO>();
foreach ( var category in categories )
{
foreach ( var item in category.Items )
{
if ( !items.Contains(item) )
items.Add(item);
}
}
// Second, get the list of items that have the category.
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Hope this helps:
var result = (Object to search in).Where(m => (Object to compare to).Any(r => r.Equals(m.Key)).ToList();

How to flatten nested objects (LINQ)

I'm doing some work on an old Winforms grid and i have two Models that i am trying to flatten and assign to a DataGridView.
Here are my sample models.
public class StockItem
{
public string StockName { get; set; }
public int Id { get; set; }
public List<Warehouse> Warehouses { get; set; }
}
public class Warehouse
{
public string WarehouseName { get; set; }
public int Id { get; set; }
}
The data works in a way that a warehouse must first be created and then assigned to each StockItem. A StockItem may have all the warehouses or may only have one.
I need to flatten the data so that the grid shows the StockName and then all the associated warehouses for the stock item.
Example
StockCode1 Warehouse1 Warehouse2 Warehouse3
StockCode2 Warehouse1 Warehouse2
StockCode2 Warehouse1 Warehouse3
I've attempted to do this via a Linq query but can only get a record per StockItem\Warehouse.
You can achieve it by creating a DataTable that yon can easily use as a source for the gridview. First add all columns and then for each stock add the warehouses:
var warehouseNames =
stocks
.SelectMany(x => x.Warehouses.Select(y => y.WarehouseName)).Distinct();
var dt = new DataTable();
dt.Columns.Add("StockCode");
foreach (var name in warehouseNames)
{
dt.Columns.Add(name);
}
foreach (var stock in stocks)
{
var row = dt.NewRow();
row["StockCode"] = stock.Id;
foreach (var warehouse in stock.Warehouses)
{
row[warehouse.WarehouseName] = warehouse.Id;
}
dt.Rows.Add(row);
}
I do not recommend it, but you can use dynamic objects to create objects with the shape you want. Doing this is not a common C# pattern. This is more common in languages like Python or Javascript.
C# is a strongly typed language and venturing into the world of dynamic objects should only be considered when absolutely necessary (think parsing a json blob). I strongly consider you reevaluate what you need to do and approach it from a different angle.
Something like this:
var availableWarehouses = new [] {
new Warehouse {
WarehouseName = "Warehouse1",
Id = 1
},
new Warehouse {
WarehouseName = "Warehouse2",
Id = 2
},
new Warehouse {
WarehouseName = "Warehouse3",
Id = 3
}
};
var stocks = new [] {
new StockItem {
StockName = "StockCode1",
Id = 1,
Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1], availableWarehouses[2] }
},
new StockItem {
StockName = "StockCode2",
Id = 2,
Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[1] }
},
new StockItem {
StockName = "StockCode3",
Id = 3,
Warehouses = new List<Warehouse> { availableWarehouses[0], availableWarehouses[2] }
}
};
var flatten = stocks.Select(item => new {
StockName = item.StockName,
WarehousesNames = availableWarehouses.Select(warehouse => item.Warehouses.Contains(warehouse) ? warehouse.WarehouseName : " ")
.Aggregate((current, next) => current + "\t" + next)
});
foreach(var item in flatten) {
Console.WriteLine("{0}\t{1}", item.StockName, item.WarehousesNames);
}
That should give you what you need:
var flattened = stockItems
.Select(x => new {
StockName = x.StockName,
WarehouseNames = x.Warehouses
.Select(y => y.WarehouseName)
.ToList() })
.ToList();
It will result in a collection of items that contain StockName and a list of WarehouseName strings. ToList added to enumerate the query.
For these sample data:
List<StockItem> stockItems = new List<StockItem>
{
new StockItem
{
StockName ="A",
Id = 1,
Warehouses = new List<Warehouse>
{
new Warehouse { Id = 1, WarehouseName = "x" },
new Warehouse { Id = 2, WarehouseName = "y" }
}
},
new StockItem
{
StockName = "B",
Id = 2,
Warehouses = new List<Warehouse>
{
new Warehouse { Id = 3, WarehouseName = "z" },
new Warehouse { Id = 4, WarehouseName = "w" }
}
}
};
I've got the following result:

c# append new items to list item

The code below has a list with two rows in it, I want to be able to amalgamate the two lines and sum the income field based on the Country.
public class City
{
public string Name {get;set;}
public string Country {get;set;}
public double income {get;set;}
}
public class myClass
{
void Main()
{
var c = GetCities();
var d = c.GroupBy(s => new {s.Country, s.Flag, s.Name}).Select(s => new City { Country = s.Key.Country, Flag = s.Key.Flag, Name = s.Key.Name});
d.Dump(); //LINQPAD output to screen
}
public List<City> GetCities()
{
List<City> cities = new List<City>();
cities.Add(new City() { Name = "Istanbul", income= 22.00, Country = "Turkey" });
cities.Add(new City() { Name = "", income= 44.88, Country = "Turkey" });
return cities;
}
}
in my real application the list is being generated in two places, but the data needs to show on one single line.
found my answer
var result = c.GroupBy(x => x.Country)
.Select(g => {
var item = g.First();
return new{
Country = item.Country,
Name = item.Name,
income = g.Sum(x => x.income) };
}).ToList();

C# where clause copy object by reference

I have a list in c# :
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
List<Item> items = new List<Item>()
{
new Item() { Id = 1, Name = "Item-1" },
new Item() { Id = 2, Name = "Item-2" },
new Item() { Id = 3, Name = "Item-3" },
new Item() { Id = 4, Name = "Item-4" },
new Item() { Id = 5, Name = "Item-5" },
};
Now i use where clause on the above list of items and fetch all items whose Id is greater than or equals to 3.
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3).ToList();
The above statement creates a new List but it copies the objects by reference, so if i change any object`s property in itemsWithIdGreaterThan3 list then it reflect the changes in item list:
itemsWithIdGreaterThan3[0].Name = "change-item-2"
This also changes the object with Id = 3 in items List.
Now what i want is to clone the object, so i found Select function like:
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3)
.Select(i => new Item() { Id = i.Id, Name = i.Name }).ToList();
This works, but what if i have an object contains 20 to 30 properties or even more. Then in than case we have to manually copy each property. Is there any shortcut solution for this problem ??
You could make a constructor for Item that takes an Item as it's parameter. Within that you would then do the property assignment. Then just call the constructor from the Select.
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public Item(Item i)
{
Id = i.Id;
Name = i.Name;
...
}
}
List<Item> itemsWithIdGreaterThan3 = items.Where(i => i.Id >= 3)
.Select(i => new Item(i)).ToList();

Filter a list by another list C#

I have the following business objects:
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
List<ItemCategoryBO> categoryList = new List<ItemCategoryBO>();
ItemCategoryBO itemCategory = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "CARS";
itemCategory.Title = "Cars";
ItemCategoryBO itemCategory2 = new ItemCategoryBO();
itemCategory.ItemCategoryCd = "PLANES";
itemCategory.Title = "Planes";
categoryList.Add(itemCategory);
categoryList.Add(itemCategory2);
List<ItemBO> itemList = new List<ItemBO>();
ItemBO item1 = new ItemBO();
item1.ItemId = 1;
item1.Title = "1st item";
item1.ItemCategoryCd = "OTHER";
ItemBO item2 = new ItemBO();
item2.ItemId = 2;
item2.Title = "2nd Item";
item2.ItemCategoryCd = "CARS";
ItemBO item3 = new ItemBO();
item3.ItemId = 3;
item3.Title = "3rd Item";
item3.ItemCategoryCd = "PLANES";
itemList.Add(item1);
itemList.Add(item2);
itemList.Add(item3);
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
If you have a situation like:
List<ItemBO> items;
List<ItemCategoryBO> categories;
and you wish to get all the items that have a category that is in your list of categories, you can use this:
IEnumerable<ItemBO> result = items.Where(item =>
categories.Any(category => category.ItemCategory.equals(item.ItemCategory)));
The Any() operator enumerates the source sequence and returns true as soon as an item satisfies the test given by the predicate. In this case, it returns true if the categories list contains an ItemCategoryBO where its ItemCategory string is the same as the item's ItemCategory string.
More information about it on MSDN
Try using some linq
List<ItemBO> itm = new List<ItemBO>;
//Fill itm with data
//get selected item from control
string selectedcategory = cboCatetories.SelectedItem;
var itms = from BO in itm where itm.ItemCategory = selectedcategory select itm;
itms now contains all items in that category
Here's something I did in Linqpad
void Main()
{
var cat1 = new ItemCategoryBO {ItemCategory="c1", Title = "c1"};
var cat2 = new ItemCategoryBO {ItemCategory="c2", Title = "c2"};
var item1 = new ItemBO { ItemId = 1, Title = "item1", ItemCategory="c1"};
var item2 = new ItemBO { ItemId = 1, Title = "item2", ItemCategory="c2"};
var item3 = new ItemBO { ItemId = 1, Title = "item3", ItemCategory="c2"};
var item4 = new ItemBO { ItemId = 1, Title = "item4", ItemCategory="c3"};
var items = new List() {item1, item2, item3, item4};
var categories = new List() {cat1, cat2};
var itemsInCategory = from item in items
join category in categories on item.ItemCategory equals category.ItemCategory into itemInCategory
from categoryItem in itemInCategory
select new {item.Title, item.ItemCategory};
itemsInCategory.Dump();
}
// Define other methods and classes here
public class ItemCategoryBO
{
public string ItemCategory { get; set; }
public string Title { get; set; }
}
public class ItemBO
{
public int ItemId { get; set; }
public string Title { get; set; }
public string ItemCategory { get; set; }
}
This returns:
Title, ItemCategory
item1 c1
item2 c2
item3 c2
Try this:
List<ItemBO> items = ...;
ItemCategoryBO category = ...;
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Updated to address OP's updated question:
If I have a list of a few categories, how could I find a list of items that contain a category in the list of categories? (In my example, I want to get back items 2 and 3)
I think you actually should do this in two steps. First, get your distinct list of items. Then, from your items, get your list of categories. So:
// First, get the distinct list of items
List<ItemBO> items = new List<ItemBO>();
foreach ( var category in categories )
{
foreach ( var item in category.Items )
{
if ( !items.Contains(item) )
items.Add(item);
}
}
// Second, get the list of items that have the category.
List<ItemBO> filteredItems = items
.Where( i => i.ItemCategory.Equals(category) )
.FirstOrDefault();
Hope this helps:
var result = (Object to search in).Where(m => (Object to compare to).Any(r => r.Equals(m.Key)).ToList();

Categories