How to select Dynamic column from List - c#

I want to select columns dynamically from List as following. So what could be the best way?
//a objects list
List<DashBoard> dashboardlist = (List<DashBoard>)objList;
string strColumns = "RecDate,ModifiedDate";
objList = (from obj in dashboardlist select new { strColumns }).ToList();
/////////////
Ok,Just forget Object List say I have database table which have number of column ID,Name,Age,sex,etc ..Then I have columnList to display and the columnList is change according to condition . SO I have List people; and List columnTemplate; so now I want to select the column based on the template .

Thanks for providing ideas to my question.Spending couple of hours in
Google I found solution .
public void Test() {
var data = new[] {
new TestData { X = 1, Y = 2, Z = 3 }
, new TestData { X = 2, Y = 4, Z = 6 }
};
var strColumns = "X,Z".Split(',');
foreach (var item in data.Select(a => Projection(a, strColumns))) {
Console.WriteLine("{0} {1}", item.X, item.Z);
}
}
private static dynamic Projection(object a, IEnumerable<string> props) {
if (a == null) {
return null;
}
IDictionary<string,object> res = new ExpandoObject();
var type = a.GetType();
foreach (var pair in props.Select(n => new {
Name = n
, Property = type.GetProperty(n)})) {
res[pair.Name] = pair.Property.GetValue(a, new object[0]);
}
return res;
}
class TestData {
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}

I assume that the list of the columns may come from an external resource and change, I propose:
With reflection you could produce a list of FieldInfo that correspond to each Column, then loop over each item on the list and each FieldInfo and call GetValue on the data object.

Here is the solution:
Select a Column Dynamically using LINQ?
and look Dynamic Linq: http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Let's supposed that you have only 2 templates. You can create a method for each template, that returns only the columns you need. Something like this:
// method for template 1 - returns only 3 columns/properties
private DashBoard CreateDashBoardXxxxxxx(DashBoard item)
{
return new DashBoard {
Property1 = item.Property1,
Property4 = item.Property2,
Property3 = item.Property3
};
}
// method for template 2 - returns N columns/properties
private DashBoard CreateDashBoardYyyyyyyy(DashBoard item)
{
return new DashBoard {
Property1 = item.Property1,
Property4 = item.Property2,
// Other properties
// .....
PropertyN = item.PropertyN
};
}
You can then use those methods like this:
List<DashBoard> dashboardlist = (List<DashBoard>)objList;
// using template 1
var list = dashboardlist.Select(CreateDashBoardXxxxxxx);
// using template 2
var list2 = dashboardlist.Select(CreateDashBoardYyyyyyyy);
You just need to do some code to decide which template should be used.
I hope this helps!!

Related

Create new list of objects for each of the item in another object list using Linq

I have an object with
public class test
{
public object obj1;
public List<string> items;
}
I have a List<test>. For each of the items i want to create a new object with properties from obj1 and an item. The items list can be null or empty. Is there a way to do in Linq?
List<test> tests = new List<test>();
var ob1=new test{ obj1 = "obj1" };
var ob2=new test{ obj1 = "obj2" };
var ob3=new test{ obj1 = "obj3" };
var ob4=new test{ obj1 = null };
tests.Add(ob1);
tests.Add(ob2);
tests.Add(ob3);
tests.Add(ob4);
var result = tests.Select(e => new NewType
{
name = e.obj1 != null ? e.obj1.ToString() : null
});
foreach (var item in result)
{
Console.WriteLine(item.name);
}
Is this what you are looking for?
Say Test.Obj1 has two properties you want to include in each new item, 'Name' & 'ID', along with an item from the list - try a query like this.
Will generate a list of new (anonymous) objects with (for example) the first element of each Test.Obj1.List
IEnumerable<dynamic> results = tests
.Select(o => new { Name = o.obj1.Name,
ID = o.obj1.Id,
Item = o.items.First() ?? null} )
.ToList();
Console.WriteLine(results.ElementAt(0));
Console.WriteLine(results.ElementAt(1));
Console.WriteLine(results.ElementAt(2));
// { Name = Foo, ID = 1, Item = a }
// { Name = Bar, ID = 2, Item = d }
// { Name = Goo, ID = 3, Item = g }
// Example types below...
public class Obj1
{
public string Name {get; set;}
public int Id {get; set;}
public Obj1(string name, int id)
{
Name = name;
Id = id;
}
}
// Create some instances
Obj1 objA = new MyObject("Foo", 1);
Obj1 objB = new MyObject("Bar", 2);
Obj1 objC = new MyObject("Goo", 3);
// Your test class that contains the custom object, and a list
public class Test
{
public Obj1 obj1;
public List<string> items;
public Test(Obj1 myobject, List<string> myItems)
{
obj1 = myobject;
items = myItems;
}
}
// Make a list of test objects
List<Test> tests = new List<Test>{
new Test(objA, new List<string>{"a", "b", "c"}),
new Test(objB, new List<string>{"d", "e", "f"}),
new Test(objC, new List<string>{"g", "h", "i"})};
For new{}, you can replace with a named type T and constructor as needed and update the results list to be
IEnumerable<T>
Also, you may have a specific criteria for which item in the list you want (or all) so you could add a more interesting query in the constructor rather than .First()
Or to just grab the whole test item adjust the constructor as
new { Obj = o.obj1, Items = o.items}
Hope this helps - there are some great links in this collection: https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-a-subquery-on-a-grouping-operation

C# custom add in a List

I have the following list of strings :
var files = new List<string> {"file0","file1","file2","file3" };
I would like to be able to add new files to this list, but if the inserted file is present in the list, I would like to insert custom value that will respect the following format $"{StringToBeInserted}"("{SomeCounter}
For instance : try to add "file0" and "file0" is already I would like to insert "file0(1)". If I try again to add "file0" ... I would like to insert with "file0(2)" and so on ... Also, I would like to provide a consistency, for instance if I delete "file0(1)" ... and try to add again "item0" ... I expect that "item0(1)" to be added. Can someone help me with a generic algorithm ?
I would use a HashSet<string> in this case:
var files = new HashSet<string> { "file0", "file1", "file2", "file3" };
string originalFile = "file0";
string file = originalFile;
int counter = 0;
while (!files.Add(file))
{
file = $"{originalFile}({++counter})";
}
If you have to use a list and the result should also be one, you can still use my set approach. Just initialize it with your list and the result list you'll get with files.ToList().
Well, you should create your own custom class for it, using the data structure you described and a simple class that includes a counter and an output method.
void Main()
{
var items = new ItemCountList();
items.AddItem("item0");
items.AddItem("item1");
items.AddItem("item2");
items.AddItem("item0");
items.ShowItems();
}
public class ItemCountList {
private List<SimpleItem> itemList;
public ItemCountList() {
itemList = new List<SimpleItem>();
}
public void DeleteItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null) {
item.Count--;
if (item.Count == 0)
itemList.Remove(item);
}
}
public void AddItem(string value) {
var item = itemList.FirstOrDefault(b => b.Value == value);
if (item != null)
item.Count++;
else
itemList.Add(new SimpleItem {
Value = value,
Count = 1
});
}
public void ShowItems() {
foreach (var a in itemList) {
Console.WriteLine(a.Value + "(" + a.Count + ")");
}
}
}
public class SimpleItem {
public int Count {get; set;}
public string Value {get; set;}
}

To check a list and populate the value accordingly

I have a string list with values. I need to assign a value to a list based on the particular index of the string. Below is my code for the same.
var fruits = new string[] { "Color", "Price", "Shape ", "Nutrients" };
var fruitDetails = db.Fruits.Where(f => f.FruitId == 5).Select(f => new FruitModel{Id = f.FruitId,Category=f.Category, Color = f.FruitColor, Price=f.FruitPrice, Shape = f.FruitShape, Nutrients = f.FruitNutrients}).FirstOrDefault();
Now I need to populate a list using the results obtained from the Linq query based on the list of fruits.
foreach (var item in fruits )
{
var fruitData = new fruitData ();
fruitData.Category= fruitDetails .Category;
fruitData.Description= ; //This has to be the value of Color if item is color,value of price if item is price and so on...
fruitList.Add(fruitData);
}
So based on what the loop value is corresponding value needs to be populated. I do not want to be using Reflection. Is there an alternate method?
What if you use a switch statement like
switch (item)
{
case "Color":
fruitData.Description = fruitDetails.Color;
break;
case "Price":
fruitData.Description = fruitDetails.Price;
break;
case "Nutrient":
fruitData.Description = fruitDetails.Nutrient;
break;
default:
break;
}
I would suggest adding a property to FruitModel that returns the description based on the instance's Category, and that can use a static Dictionary to map categories to accessor functions:
public class FruitModel {
public int Id;
public string Category;
public string Color;
public double Price;
public string Shape;
public string Nutrients;
static Dictionary<string, Func<FruitModel, string>> catmap = new Dictionary<string, Func<FruitModel, string>> {
{ "Color", fm => fm.Color },
{ "Price", fm => fm.Price.ToString() },
{ "Shape", fm => fm.Shape },
{ "Nutrients", fm => fm.Nutrients },
};
public string Description {
get => catmap[Category](this);
}
public static List<string> FruitDetailCategories {
get => catmap.Keys.ToList();
}
}
You can also create a static property to return the detail categories rather than put the list somewhere else.
(Obviously you could use the switch instead of the Dictionary if preferred in the property body, but it doesn't lend itself to providing the detail categories.)
Now you can build your list easily:
var fruitList = new List<FruitData>();
foreach (var fruit in fruitDetails) {
var fd = new FruitData();
fd.Category = fruit.Category;
fd.Description = fruit.Description;
fruitList.Add(fd);
}

Define list of objects. Get by specific field in object

How can we create a list of objects in C# and acces them by a specific field inside this object?
For example take this object:
class Section
{
public string Name { get; }
public long Size { get; }
public Section(string name, long size)
{
Name = name;
Size = size;
}
}
I would like to create a list of these objects which I can access by Section.Name.
I can create a dictionary like:
private static readonly Dictionary<string, Section> validSections = new Dictionary<string, Section>
{
{ "section-a", new Section("section-a", 1) },
{ "section-b", new Section("section-b", 2) },
{ "section-c", new Section("section-c", 3) },
{ "section-d", new Section("section-d", 4) },
};
But as you see, I have to declare the section name twice, which looks inelegant. Is there a more elegant way?
But as you see, I have to declare the section name twice, which looks
inelegant. Is there a more elegant way?
To avoid repetitve typing you can create dictionary from collection of sections via ToDictionary call:
private static readonly Dictionary<string, Section> validSections = new[] {
new Section("section-a", 1),
new Section("section-b", 2),
new Section("section-c", 3),
new Section("section-d", 4)
}.ToDictionary(s => s.Name);
If this is not time critical then you can use List<Section> list = new ArrayList<Section>(); and store data in it.
Later you can use LINQ to query based on name .where(x=>x.Name=="somename")
First of all your Model class can look like:
class Section
{
public string Name { get; set; }
public long Size { get; set; }
}
You don't need the Name twice so you can just create a list:
private static List<Section> myList = new List<Section>();
myList.add(new Section {Name = "section-a", Size = 1});
// do this for all the sections ...
Then as other answers suggest you can use LINQ:
myList.Single(s => s.Name == "section-a");
Will simply return the single element where the name is "section-a".
Read more about LINQ here: https://msdn.microsoft.com/en-us/library/bb308959.aspx
You could write a function that takes a list of sections and returns the corresponding dictionary. Something like:
public static Dictionary<string, Section> SectionDictionary(List<Section> sections) {
var dict = new Dictionary<string, Section>();
foreach (var section in sections)
dict.Add(section.Name, section);
return dict;
}
You can just access the elements using LINQ:
var list = ...;
var el = list.FirstOrDefault(o => o.Name = nameValue);
Or you can create a (collection) class that implements your own indexer / getter logic. E.g. (pseudocode)
public class MyCollection : Collection<Section>
{
public Section this[string nameValue]
{
get
{
return this.FirstOrDefault(o => o.Name == nameValue);
}
}
}
Then the usage is:
var coll = new MyCollection() ....;
var el = coll["Some name"];

Merge two lists in C#

I want to merge two lists with different attributes into one list, but while merging it, I want to check if there is, in this particular example, exact date that is in the both lists, and if there is, I want to take both attributes from that elements, and merge them into one element in another list
List 1:
List<object> r1 = (from x in sp1 select new
{
x.Imported,
x.Period
}).ToList<object>();
L1 result:
List 2:
List<object> r2 = (from x in sp2 select new
{
x.Dissolution,
x.Period
}).ToList<object>();
L2 result:
Wanted result:
For now, this is how i merge r1 and r2:
List<object> r3 = new List<object>(r1.Concat(r2));
You could transform them into same type and use stuff like this
r1
.Select(x => new { Imported = x.Imported, Dissolution = null, Period = x.Period)
.Concat(
r2.Select(x => new { Imported = null, Dissolution = x.Dissolution, Period = x.Period))
.GroupBy(x => x.Period)
.Select(x => new { Imported = x.Max(e => e.Imported),
Dissolution = x.Max(e => e.Dissolution),
Period = x.Key);
Create a Dictionary
Dictionary MyDict<String, List<Object>>;
MyDict[object.Perdiod].Add(object);
For each date there will be an entry in the dictionnary, and it will at this "date index" keep a list of all object that happens at that period.
Easiest way IMO and it does not need to make a O(n) check for every entry added
Just make sure when you add data that it is not null IE
MyDict[Object.Period] != null
Plus, has Nikhil Agrawal said I would not use Object to keep a list of thing... it feels wrong and is error prone. you might want to declare an abstract class that will be used like an Interface or simply an interface for those items (object).
AFAK you need reflection to achieve this as the name for Anonymous type is assigned at Compile time here an example on how you can achieve what you want
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml.Linq;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
List<ImportedType> sp1 = new List<ImportedType>();
List<DissolutionType> sp2 = new List<DissolutionType>();
sp1.AddRange( new ImportedType[]{new ImportedType() { Imported = 2, Period = "2024-02" }, new ImportedType() { Imported = 2, Period = "2014-11" }, new ImportedType() { Imported = 2, Period = "2024-12" }});
sp2.AddRange(new DissolutionType[] { new DissolutionType() { Dissolution = 2, Period = "2024-02" }, new DissolutionType() { Dissolution = 2, Period = "2034-02" }, new DissolutionType() { Dissolution = 2, Period = "2024-12" } });
var r1 = (from x in sp1
select new
{
x.Imported,
x.Period
}).ToList<object>();
var r2 = (from x in sp2
select new
{
x.Dissolution,
x.Period
}).ToList<object>();
var r3 = r1.Concat(r2).Except(r1.Where(res =>
{
object vp2 = r2.SingleOrDefault(res2 => GetValue(res2) == GetValue(res));
if (vp2!=null)
{
return true;
}
return false;
}));
}
private static object GetValue(object res)
{
Type t = res.GetType();
PropertyInfo p = t.GetProperty("Period");
object v = p.GetValue(res, null);
return v;
}
}
}
//here I suppose that you implements 2 classes like this
public class ImportedType
{
public int Imported { get; set; }
public string Period { get; set; }
}
public class DissolutionType
{
public int Dissolution { get; set; }
public string Period { get; set; }
}
Result
I agree with Nikhil Agrawal in that the code now really needs some fixing up, as it is really hard to work with anonymous types, especially since they have been cast to object.
Ignoring that, and accepting it as a challenge (use anonymous types cast to object), this is what I have come up with:
Code that merges does a full outer join:
Func<object, object> getPeriodKey = first =>
{
var periodProperty = first.GetType().GetProperty("Period");
return periodProperty.GetValue(first);
};
var temp = r1.GroupJoin(r2, getPeriodKey, getPeriodKey, (obj, tInner) =>
{
dynamic right = tInner.FirstOrDefault();
if (right == null)
return (object)(new
{
Period = ((dynamic)obj).Period,
Imported = ((dynamic)obj).Imported,
});
else
return (object)(new
{
Period = ((dynamic)obj).Period,
Imported = ((dynamic)obj).Imported,
Dissolution = (int?)right.Dissolution,
});
});
var merged = temp.Union(r2, new RComparer());
and the required comparer is below:
class RComparer : IEqualityComparer<object>
{
public bool Equals(object x, object y)
{
var xPeriodProperty = x.GetType().GetProperty("Period");
var yPeriodProperty = y.GetType().GetProperty("Period");
if (xPeriodProperty != null && yPeriodProperty != null)
{
var xPeriod = (string)xPeriodProperty.GetValue(x);
var yPeriod = (string)yPeriodProperty.GetValue(y);
return xPeriod == yPeriod;
}
else
return base.Equals(y);
}
public int GetHashCode(object obj)
{
var periodProperty = obj.GetType().GetProperty("Period");
if (periodProperty != null)
//This will essentially hash the string value of the Period
return periodProperty.GetValue(obj).GetHashCode();
else
return obj.GetHashCode();
;
}
}

Categories