How to convert to generic code? - c#

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

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

Dynamically add Or conditions

I have a collection where I want to programatically add OR condtions to a linq query. I know how to add AND condtions like the following:
var mySet = SomeFactory.GetData();
foreach(var nameFilter in nameFilters)
{
mySet = mySet.Where(item => item.Name == nameFilter);
}
foreach(var ageFilter in ageFilters)
{
mySet = mySet.Where(item => item.Age == ageFilter)
}
however, if I wanted these to be OR conditions rather than be 'AND' together, how would I do that? I.E. I can do this if I know I will always have both and never an array of different values:
mySet.Where(item => item.Name == nameFilter[0] || item.Name == nameFilter[1] ... || item.Age == ageFilter[0] || item.Age == ageFilter[1] || ...);
TL;DR: I want to be able to chain an unknown number of boolean checks into a single expression evaluated with OR statements. For example, if I have a cross reference of People named Mary or Jim who are either 32 or 51.
PredicateBuilder would help you to apply where clauses in flexibility. You can find extension method here.
var filterOfNames = new List<string> {"Sample", "Sample2"};
var filterOfAges = new List<int> { 21, 33, 45 };
var mySet = SomeFactory.GetData();
var predicate = PredicateBuilder.True<TypeOfmySet>();
foreach (var filterOfName in filterOfNames)
{
//If it is the first predicate, you should apply "And"
if (predicate.Body.NodeType == ExpressionType.Constant)
{
predicate = predicate.And(x => x.Name == filterOfName);
continue;
}
predicate = predicate.Or(x => x.Name == filterOfName);
}
foreach (var filterOfAge in filterOfAges)
{
//If it is the first predicate, you should apply "And"
if (predicate.Body.NodeType == ExpressionType.Constant)
{
predicate = predicate.And(x => x.Age == filterOfAge);
continue;
}
predicate = predicate.Or(x => x.Age == filterOfAge);
}
//I don't know the myset has which type in IQueryable or already retrieved in memory collection. If it is IQueryable, don't compile the predicate otherwise compile it.
//var compiledPredicate = predicate.Compile();
mySet = mySet.Where(predicate);
There is no additive "or" in LINQ. Combining "Where" expressions is always evaluated as "and".
However, you can build predicates, add them to a list, then test them. I think this code does what you want:
using System;
using System.Collections.Generic;
class Item
{
internal string Name { get; set; }
internal short Age { get; set; }
internal string City { get; set; }
}
public static class ExtensionMethods
{
public static List<T> FindAll<T>(this List<T> list, List<Predicate<T>> predicates)
{
List<T> L = new List<T>();
foreach (T item in list)
{
foreach (Predicate<T> p in predicates)
{
if (p(item)) L.Add(item);
}
}
return L;
}
}
class Program
{
static void Main(string[] args)
{
List<Item> items = new List<Item>();
items.Add(new Item { Name = "Bob", Age = 31, City = "Denver" });
items.Add(new Item { Name = "Mary", Age = 44, City = "LA" });
items.Add(new Item { Name = "Sue", Age = 21, City = "Austin" });
items.Add(new Item { Name = "Joe", Age = 55, City = "Redmond" });
items.Add(new Item { Name = "Tom", Age = 81, City = "Portland" });
string nameFilter = "Bob,Mary";
//string ageFilter = "55,21";
string ageFilter = null;
string cityFilter = "Portland";
List<Predicate<Item>> p = new List<Predicate<Item>>();
if (nameFilter != null)
{
p.Add(i => nameFilter.Contains(i.Name));
}
if (cityFilter != null)
{
p.Add(i => cityFilter.Contains(i.City));
}
if (ageFilter != null)
{
p.Add(i => ageFilter.Contains(i.Age.ToString()));
}
var results = items.FindAll(p);
foreach (var result in results)
{
Console.WriteLine($"{result.Name} {result.Age} {result.City}");
}
}
}

C# Lambda Expressions Using Complex Objects

How can I use a Lambda Expression on a List of Payment objects that have a List PaymentFields object. Here's what I currently have.
var list = paymentList.Where(payment => payment.PaymentFields.Any(field => field.FieldName == "ItemA" && field.FieldValue == "50");
That gives me payments that have ItemA as the Field Name and 50 as the Field Value. However, I want to COMPARE the two PaymentFields like so...
Where FieldName == "ItemA" && FieldValue = "50" && FieldName ItemA < FieldName ItemB
How would I do this?
I have two Objects:
public class Payment
{
public int Id { set; get; }
public string Name { set; get; }
public List<PaymentFields> PaymentFields { set; get; }
}
public class PaymentFields
{
public string FieldName { set; get; }
public string FieldValue { set; get; }
}
Here is an example Object:
var payment = new Payment()
{
Id = 1,
Name = "Test",
PaymentFields = new List<PaymentFields>()
{
new PaymentFields()
{
FieldName = "ItemA",
FieldValue = "20"
},
new PaymentFields()
{
FieldName = "ItemB",
FieldValue = "50"
}
}
};
Thanks for your help!
If you are absolutely sure there will be an "ItemA" and "ItemB", then this will work.
If either "ItemA" or "ItemB" is missing, it will throw an exception.
var list = paymentList
.Where(payment => payment.PaymentFields.Any(field => field.FieldName == "ItemA" && field.FieldValue == "50")
.Where(payment => payment.PaymentFields.First(field => field.FieldName == "ItemA").FieldValue
<
payment.PaymentFields.First(field => field.FieldName == "ItemB").FieldValue)
);

Select an attribute from object in a List inside another List

Can I perform a select using ternary operator to get an attribute from object inside a list?
Here is my model:
public class Xpto
{
public List<Son> Sons { get; set; }
}
public class Son
{
public string Names { get; set; }
}
And here i would like to get "Name" attribute for each son that i have:
var result = (from a in mylist
select new
{
sonsNames = a.Sons == null : <What should i put here?>
}).ToList<object>();
I've tried Sons.ToString() but it prints an object reference.
I would like to have a string list in "sonsNames" and each name separeted by a ','. Example: sonsName: 'george, john'.
what about this ?
//set up a collection
var xptos = new List<Xpto>()
{ new Xpto()
{ Sons = new List<Son>
{ new Son() { Names = "test1" },
new Son() { Names = "test2" }
}
},
new Xpto()
{
Sons = new List<Son> {
new Son() { Names = "test3" }
}
}};
//select the names
var names = xptos.SelectMany(r => r.Sons).Where(k => k.Names != null)
.Select(r => r.Names + ",") .ToList();
names.ForEach(n => Console.WriteLine(n));
Here's more info on SelectMany()

Create a list from a DataTable

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

Categories