Create a list from a DataTable - c#

I have a entity as follow:
public class MarketDataBEO
{
public MarketDataBEO()
{
childDetails = new List<MarketDataBEO>();
}
public string MarketID { get; set; }
public string MarketHeirarchyID { get; set; }
public string MarketName { get; set; }
public string TotalMarketSizeCYM1GI { get; set; }
public List<MarketDataBEO> childDetails { get; set; }
}
the relation is between MarketID and MarketHeirarchyID
a child can also have another child list
how do i create a multilevel list ?
EDIT:
MarketID | MarketHeirarchyID
1 NULL
2 NULL
3 2
4 2
5 8 <--
6 5
7 NULL
8 7
9 7
10 8

var dict = dt.AsEnumerable()
.ToDictionary(
row => row[0].ToString(),
row => {
return new MarketDataBEO()
{
MarketID = row[0].ToString(),
MarketHeirarchyID = row[1].ToString()
// Other class members here
// MarketName = row[2].ToString()
// TotalMarketSizeCYM1GI = row[3].ToString()
}
);
foreach (var m in dict.Values)
{
if (!string.IsNullOrEmpty(m.MarketHeirarchyID))
dict[m.MarketHeirarchyID].childDetails.Add(dict[m.MarketID]);
}
var result = dict.Values.ToList();

Pseudo-code:
foreach(var x in datatable.Select(d=>d.MarketHerarchyId!="").ToList())
{
datatable.Find(x.MarketHeirarchyId).childDetails.Add(x);
datatable.Remove(x);
}

Try this:
var marketDataBEO = new MarketDataBEO() { MarketID = "0" };
var dictionary = new Dictionary<string, MarketDataBEO>();
dt.Rows
.Cast<DataRow>()
.OrderBy(dr => Convert.ToInt32(dr["MarketID"].ToString()))
.ToList().ForEach(dr =>
{
var m = new MarketDataBEO()
{
MarketID = dr["MarketID"].ToString(),
MarketHeirarchyID = dr["MarketHeirarchyID"].ToString()
};
if (dr["MarketHeirarchyID"].ToString() == "")
{
marketDataBEO.childDetails.Add(m);
}
else
{
dictionary[dr["MarketHeirarchyID"].ToString()].childDetails.Add(m);
}
dictionary.Add(m.MarketID, m);
});
[EDIT] Based on your updated info, here's a 2-step approach. First, you'd get all the rows inside a dictionary, then you'd parse the KeyValuePair collection and create your hierarchical object.
var marketDataBEO = new MarketDataBEO() { MarketID = "0" };
var dictionary = new Dictionary<string, MarketDataBEO>();
dt.Rows.Cast<DataRow>()
.ToList().ForEach(dr =>
{
var m = new MarketDataBEO()
{
MarketID = dr["MarketID"].ToString(),
MarketHeirarchyID = dr["MarketHeirarchyID"].ToString()
};
dictionary.Add(m.MarketID, m);
});
dictionary.ToList().ForEach(kvp =>
{
if (kvp.Value.MarketHeirarchyID == "")
{
marketDataBEO.childDetails.Add(kvp.Value);
}
else
{
dictionary[kvp.Value.MarketHeirarchyID].childDetails.Add(kvp.Value);
}
});

Related

Cannot implicity convert type 'System.Collections.Generic.List<string>' to 'System.Collections.Generic.IList<LM_WebApi.Controllers.Items>

Having problem on my web api "Cannot implicity convert type IQueryable<> to Generic.List". I'm getting the data from Entity Framework.
When I put ToList() it return this error: System.Collections.Generic.List' to 'System.Collections.Generic.IList<LM_WebApi.Controllers.Items>
public IList<Sales_Data> GetSalesData()
{
using (var db = new LM_ReportEntities())
{
var list = db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1);
IList<Sales_Data> salesData = new List<Sales_Data>();
salesData.Add(new Sales_Data { Items = list.Select(x => x.Item_Number).ToList() });
salesData.Add(new Sales_Data { Periods = list.Select(x => x.Period) });
return salesData;
}
}
Here are my class:
public class Items
{
public string Item_No { get; set; }
}
public class Periods
{
public string Period { get; set; }
}
My model:
public class Sales_Data
{
public IList<Items> Items { get; set; }
public IList<Periods> Periods { get; set; }
}
I want to return the data from Items and Periods as List.
You have to do this in the following way:
var list = db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1).ToList();
In the next step, populate the lists of Items and Periods types from list collection.
var ItemsList = new List<Items>();
var PeriodsList = new List<Periods>();
foreach (var i in list)
{
Items item = new Items() { Item_No = i.Item_Number };
Periods period = new Periods() { Period = i.Period };
ItemsList.Add(item);
PeriodsList.Add(period);
}
Finally, populate Sales_Data model by the above lists.
var salesData = new Sales_Data() { Items = ItemsList, Periods = PeriodsList };
change db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1) to db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1).ToList()
Edit:
var list = db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1);
...
salesData.Add(new Sales_Data { Items = list.Select(x => new Items{Item_No = x.Item_Number}).ToList() });
salesData.Add(new Sales_Data { Periods = list.Select(x => new Periods{Periods = x.Period}).ToList() });
First of all, I think, you made a mistake in model, change it this way:
public class Sales_Data
{
public string Item_No { get; set; }
public string Period { get; set; }
}
And then change your method like this:
public IList<Sales_Data> GetSalesData()
{
using (var db = new LM_ReportEntities())
{
var list = db.OE_Invoiced_BaseProducts.Where(x => x.Year == "2020" && x.Period == 1).ToList();
return (from baseProduct in db.OE_Invoiced_BaseProducts
where baseProduct.Year = "2020"
&& baseProduct.Period == 1
select new Sales_Data()
{
Item_No = baseProduct.Item_Number,
Period = baseProduct.Period
}).ToList();
}
}

Best approach to compare if one list is subset of another in C#

I have the below two classes:
public class FirstInner
{
public int Id { get; set; }
public string Type { get; set; }
public string RoleId { get; set; }
}
public class SecondInner
{
public int Id { get; set; }
public string Type { get; set; }
}
Again, there are lists of those types inside the below two classes:
public class FirstOuter
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<FirstInner> Inners { get; set; }
}
public class SecondOuter
{
public int Id { get; set; }
public string Name { get; set; }
public List<SecondInner> Inners { get; set; }
}
Now, I have list of FirstOuter and SecondOuter. I need to check if FirstOuter list is a subset of SecondOuter list.
Please note:
The names of the classes cannot be changed as they are from different systems.
Some additional properties are present in FirstOuter but not in SecondOuter. When comparing subset, we can ignore their presence in SecondOuter.
No.2 is true for FirstInner and SecondInner as well.
List items can be in any order---FirstOuterList[1] could be found in SecondOuterList[3], based on Id, but inside that again need to compare that FirstOuterList[1].FirstInner[3], could be found in SecondOuterList[3].SecondInner[2], based on Id.
I tried Intersect, but that is failing as the property names are mismatching. Another solution I have is doing the crude for each iteration, which I want to avoid.
Should I convert the SecondOuter list to FirstOuter list, ignoring the additional properties?
Basically, here is a test data:
var firstInnerList = new List<FirstInner>();
firstInnerList.Add(new FirstInner
{
Id = 1,
Type = "xx",
RoleId = "5"
});
var secondInnerList = new List<SecondInner>();
secondInner.Add(new SecondInner
{
Id = 1,
Type = "xx"
});
var firstOuter = new FirstOuter
{
Id = 1,
Name = "John",
Title = "Cena",
Inners = firstInnerList
}
var secondOuter = new SecondOuter
{
Id = 1,
Name = "John",
Inners = secondInnerList,
}
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
Need to check if firstOuterList is part of secondOuterList (ignoring the additional properties).
So the foreach way that I have is:
foreach (var item in firstOuterList)
{
var secondItem = secondOuterList.Find(so => so.Id == item.Id);
//if secondItem is null->throw exception
if (item.Name == secondItem.Name)
{
foreach (var firstInnerItem in item.Inners)
{
var secondInnerItem = secondItem.Inners.Find(sI => sI.Id == firstInnerItem.Id);
//if secondInnerItem is null,throw exception
if (firstInnerItem.Type != secondInnerItem.Type)
{
//throw exception
}
}
}
else
{
//throw exception
}
}
//move with normal flow
Please let me know if there is any better approach.
First, do the join of firstOuterList and secondOuterList
bool isSubset = false;
var firstOuterList = new List<FirstOuter> { firstOuter };
var secondOuterList = new List<SecondOuter> { secondOuter };
var jointOuterList = firstOuterList.Join(
secondOuterList,
p => new { p.Id, p.Name },
m => new { m.Id, m.Name },
(p, m) => new { FOuterList = p, SOuterList = m }
);
if(jointOuterList.Count != firstOuterList.Count)
{
isSubset = false;
return;
}
foreach(var item in jointOuterList)
{
var jointInnerList = item.firstInnerList.Join(
item.firstInnerList,
p => new { p.Id, p.Type },
m => new { m.Id, m.type },
(p, m) => p.Id
);
if(jointInnerList.Count != item.firstInnerList.Count)
{
isSubset = false;
return;
}
}
Note: I am assuming Id is unique in its outer lists. It means there will not be multiple entries with same id in a list. If no, then we need to use group by in above query
I think to break the question down..
We have two sets of Ids, the Inners and the Outers.
We have two instances of those sets, the Firsts and the Seconds.
We want Second's inner Ids to be a subset of First's inner Ids.
We want Second's outer Ids to be a subset of First's outer Ids.
If that's the case, these are a couple of working test cases:
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsTrue(isInnerSubset);
Assert.IsTrue(isOuterSubset);
}
[TestMethod]
public void ICanSeeWhenInnerAndOuterCollectionsAreNotSubsets()
{
HashSet<int> firstInnerIds = new HashSet<int>(GetFirstOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> firstOuterIds = new HashSet<int>(GetFirstOuterList().Select(outer => outer.Id).Distinct());
HashSet<int> secondInnerIds = new HashSet<int>(GetSecondOuterList().SelectMany(outer => outer.Inners.Select(inner => inner.Id)).Distinct());
HashSet<int> secondOuterIds = new HashSet<int>(GetSecondOuterList().Select(outer => outer.Id).Distinct());
firstInnerIds.Clear();
firstInnerIds.Add(5);
firstOuterIds.Clear();
firstOuterIds.Add(5);
bool isInnerSubset = secondInnerIds.IsSubsetOf(firstInnerIds);
bool isOuterSubset = secondOuterIds.IsSubsetOf(firstOuterIds);
Assert.IsFalse(isInnerSubset);
Assert.IsFalse(isOuterSubset);
}
private List<FirstOuter> GetFirstOuterList() { ... }
private List<SecondOuter> GetSecondOuterList() { ... }

LINQ Query to Filter Items By Criteria From Multiple Lists

I'm having trouble conceptualizing something that should be fairly simple using LINQ. I have a collection that I want to narrow down, or filter, based on the id values of child objects.
My primary collection consists of a List of Spots. This is what a spot looks like:
public class Spot
{
public virtual int? ID { get; set; }
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual string TheGood { get; set; }
public virtual string TheBad { get; set; }
public virtual IEnumerable<Season> Seasons { get; set; }
public virtual IEnumerable<PhotographyType> PhotographyTypes { get; set; }
}
I'm trying to filter the list of Spots by PhotographyType and Season. I have a list of ids for PhotographyTypes and Seasons, each in an int[] array. Those lists look like this:
criteria.PhotographyTypeIds //an int[]
criteria.SeasonIds //an int[]
I want to build a collection that only contains Spots with child objects (ids) matching those in the above lists. The goal of this functionality is filtering a set of photography spots by type and season and only displaying those that match. Any suggestions would be greatly appreciated.
Thanks everyone for the suggestions. I ended up solving the problem. It's not the best way I'm sure but it's working now. Because this is a search filter, there are a lot of conditions.
private List<Spot> FilterSpots(List<Spot> spots, SearchCriteriaModel criteria)
{
if (criteria.PhotographyTypeIds != null || criteria.SeasonIds != null)
{
List<Spot> filteredSpots = new List<Spot>();
if (criteria.PhotographyTypeIds != null)
{
foreach (int id in criteria.PhotographyTypeIds)
{
var matchingSpots = spots.Where(x => x.PhotographyTypes.Any(p => p.ID == id));
filteredSpots.AddRange(matchingSpots.ToList());
}
}
if (criteria.SeasonIds != null)
{
foreach (int id in criteria.SeasonIds)
{
if (filteredSpots.Count() > 0)
{
filteredSpots = filteredSpots.Where(x => x.Seasons.Any(p => p.ID == id)).ToList();
}
else
{
var matchingSpots = spots.Where(x => x.Seasons.Any(p => p.ID == id));
filteredSpots.AddRange(matchingSpots.ToList());
}
}
}
return filteredSpots;
}
else
{
return spots;
}
}
You have an array of IDs that has a Contains extension method that will return true when the ID is in the list. Combined with LINQ Where you'll get:
List<Spot> spots; // List of spots
int[] seasonIDs; // List of season IDs
var seasonSpots = from s in spots
where s.ID != null
where seasonIDs.Contains((int)s.ID)
select s;
You can then convert the returned IEnumerable<Spot> into a list if you want:
var seasonSpotsList = seasonSpots.ToList();
This may helps you:
List<Spot> spots = new List<Spot>();
Spot s1 = new Spot();
s1.Seasons = new List<Season>()
{ new Season() { ID = 1 },
new Season() { ID = 2 },
new Season() { ID = 3 }
};
s1.PhotographyTypes = new List<PhotographyType>()
{ new PhotographyType() { ID = 1 },
new PhotographyType() { ID = 2 }
};
Spot s2 = new Spot();
s2.Seasons = new List<Season>()
{ new Season() { ID = 3 },
new Season() { ID = 4 },
new Season() { ID = 5 }
};
s2.PhotographyTypes = new List<PhotographyType>()
{ new PhotographyType() { ID = 2 },
new PhotographyType() { ID = 3 }
};
List<int> PhotographyTypeIds = new List<int>() { 1, 2};
List<int> SeasonIds = new List<int>() { 1, 2, 3, 4 };
spots.Add(s1);
spots.Add(s2);
Then:
var result = spots
.Where(input => input.Seasons.All
(i => SeasonIds.Contains(i.ID))
&& input.PhotographyTypes.All
(j => PhotographyTypeIds.Contains(j.ID))
).ToList();
// it will return 1 value
Assuming:
public class Season
{
public int ID { get; set; }
//some codes
}
public class PhotographyType
{
public int ID { get; set; }
//some codes
}

How to convert to generic code?

I am trying to make a treeView that have items in the following order.
+ Continent
+ Country
+ Province
+ Territory
And, I have a generic class to hold the treeView Items data.
public class TreeViewContinents
{
public List<TreeViewContinents> Children { get; set; }
public TreeViewContinents Parent { get; set; }
public string Name { get; set; }
public string Content { get; set; }
}
Data:
private readonly List<Dictionary<string, object>> continentsList = new List<Dictionary<string, object>>();
/*
continentsList[0] - "Continent", "Asia"
"Country", "Afghanistan"
"Province", "Kabul"
"Territory", "Bagrami"
"Language", "aaa"
"Culture", "bbb"
.
.
.
.
continentsList[n] - "Continent", "North America"
"Country", "Canada"
"Province", "Ontario"
"Territory", "Ottawa"
"Language", "aaa"
"Culture", "bbb
*/
String array based on which treeView Items needs to be generated.
var treeViewItemNames = new[] { "Continent", "Country", "Province", "Territory" };
Code:
List<object> Continents = this.continentsList.Where(
p_oDict => p_oDict.ContainsKey(treeViewItemNames[0]))
.Select( p_oDict => p_oDict[treeViewItemNames[0]])
.Distinct()
.ToList();
var topItems = new List<TreeViewContinents>();
foreach (object continent in Continents)
{
var level1Items = new TreeViewContinents { Name = treeViewItemNames[0], Content = continent.ToString() };
List<object> Countries = this.continentsList.Where(
p_oDict => p_oDict.ContainsKey(treeViewItemNames[0])
&& p_oDict.ContainsKey(treeViewItemNames[1])
&& p_oDict[treeViewItemNames[0]].Equals(continent)
.Select( p_oDict => p_oDict[treeViewItemNames[1]])
.Distinct()
.ToList();
Countries.Sort((p_oU1, p_oU2) => string.Compare(p_oU1.ToString(), p_oU2.ToString(), StringComparison.Ordinal));
foreach (object country in Countries)
{
var level2Items = new TreeViewContinents { Name = treeViewItemNames[1], Content = country.ToString(), Parent = level1Items };
level1Items.Children.Add(level2Items);
List<object> Provinces = this.continentsList.Where(
p_oDict => p_oDict.ContainsKey(treeViewItemNames[0])
&& p_oDict.ContainsKey(treeViewItemNames[1])
&& p_oDict.ContainsKey(treeViewItemNames[2])
&& p_oDict[treeViewItemNames[0]].Equals(continent)
&& p_oDict[treeViewItemNames[1]].Equals(country))
.Select( p_oDict => p_oDict[treeViewItemNames[2]]).Distinct()
.ToList();
Provinces.Sort((p_oU1, p_oU2) => string.Compare(p_oU1.ToString(), p_oU2.ToString(), StringComparison.Ordinal));
foreach (object province in Provinces)
{
var level3Items = new TreeViewContinents { Name = treeViewItemNames[2], Content = province.ToString(), Parent = level2Items };
level2Items.Children.Add(level3Items);
List<object> Territories = this.continentsList.Where(
p_oDict => p_oDict.ContainsKey(treeViewItemNames[0])
&& p_oDict.ContainsKey(treeViewItemNames[1])
&& p_oDict.ContainsKey(treeViewItemNames[2])
&& p_oDict.ContainsKey(treeViewItemNames[3])
&& p_oDict[treeViewItemNames[0]].Equals(continent)
&& p_oDict[treeViewItemNames[1]].Equals(country)
&& p_oDict[treeViewItemNames[2]].Equals(province)).Select(
p_oDict => p_oDict[treeViewItemNames[3]])
.Distinct()
.ToList();
Territories.Sort((p_oU1, p_oU2) => string.Compare(p_oU1.ToString(), p_oU2.ToString(), StringComparison.Ordinal));
foreach (object territory in Territories)
{
var level4Items = new TreeViewContinents { Name = treeViewItemNames[3], Content = territory.ToString(), Parent = level3Items };
level3Items.Children.Add(level4Items);
}
}
}
topItems.Add(level1Items);
}
In the above code, I have four foreach loops to build a treeview. Is it possible to write a single loop to generate the treeView based on the the variable treeViewItemNames, so that in future If i change the variable like
var treeViewItemNames = new[] { "Continent", "Country", "Territory" };
it should generate treeView like this
+ Continent
+ Country
+ Territory
Ideas/ Suggestions please.
I suggest to iterage through continentsList elements.
var treeViewItemNames = new[] { "Continent", "Country", "Province", "Territory" };
var topItems = new List<TreeViewContinents>();
foreach (var continent in continentsList)
{
List<TreeViewContinents> currentLevel = topItems;
TreeViewContinents parentItem = null;
foreach (var sectionTitle in treeViewItemNames)
{
String value = Convert.ToString(continent[sectionTitle]);
TreeViewContinents currentItem = currentLevel.FirstOrDefault(tree => tree.Content == value);
if (currentItem == null)
{
currentItem = new TreeViewContinents { Name = sectionTitle, Content = value };
currentItem.Children = new List<TreeViewContinents>();
if (parentItem != null)
{
currentItem.Parent = parentItem;
}
currentLevel.Add(currentItem);
}
parentItem = currentItem;
currentLevel = currentItem.Children;
}
}
The problem with this code is that there is no sorting in items, but we can sort the resulting TreeViewContinents list. Here is recursive method for sorting:
public List<TreeViewContinents> SortTreeView(List<TreeViewContinents> treeViewList)
{
foreach (var item in treeViewList)
{
if (item.Children.Count > 0)
{
item.Children = SortTreeView(item.Children);
}
}
return treeViewList.OrderBy(it => it.Content).ToList();
}
You can use it after topItems list is full:
topItems = SortTreeView(topItems);

Order Objects with outer and sometimes inner position

I have two classes:
public class OuterObject
{
public int OuterPosition { get; set; }
public InnerObject InnerObject { get; set; }
}
public class InnerObject
{
public int InnerPosition { get; set; }
}
I need to order a list of OuterObjects: first, after OuterPosition. Then, only OuterObjects which have a InnerObject, should be ordered after the InnerPosition of these.
For example, if I have four OuterObjects:
A { OuterPosition = 1, new InnerObject { InnerPosition = 2 } }
B { OuterPosition = 2 }
C { OuterPosition = 3, new InnerObject { InnerPosition = 1 } }
D { OuterPosition = 4 }
I should get the order:
C { OuterPosition = 3, new InnerObject { InnerPosition = 1 } }
B { OuterPosition = 2 }
A { OuterPosition = 1, new InnerObject { InnerPosition = 2 } }
D { OuterPosition = 4 }
Is there an easy way to accomplish it?
Something like this ought to work.
// First, order by outer position
var result = list.OrderBy(o => o.OuterPosition).ToList();
// Next, rearrange the items with inner objects
var outerPositions = result.Select((o, i) => new{i, o});
var withInner = outerPositions.Where(e => e.o.InnerObject != null).ToList();
var withInnerPositions = withInner.OrderBy(e => e.o.InnerObject.InnerPosition)
.Select((e, i) => new{newPosition = withInner[i].i, e.o});
foreach(var p in withInnerPositions)
{
result[p.newPosition] = p.o;
}
IEnumerable<OuterObject> result = source
.OrderBy(obj => obj.OuterPostion)
.OrderBy(obj => obj.InnerObject != null ? obj.InnerObject.InnerPosition : obj.OuterPosition)
//the second OrderBy preserves the original order in the event of ties in the second.
IEnumerable<OuterObject> result = source
.OrderBy(obj => obj.InnerObject != null ? obj.InnerObject.InnerPosition : obj.OuterPosition)
.ThenBy(obj => obj.OuterPostion)
//ThenBy breaks ties in the first order with sub-sorting.
These may not give the order stipulated in the question, but some arrangment of these conditionals and OrderBy/ThenBy can be found to do so.

Categories