The following code takes a collection of class C and creates a collection consisting of the values of two properties A and B. A and B are put inside the same collection:
class A
{
public int x { get; set; }
}
class B
{
public int x { get; set; }
}
class C
{
public A A { get; set; }
public B B { get; set; }
}
..........
var items = new List<C>()
{
new C()
{
A = new A() {x = 1},
B = new B() {x = 2}
},
new C()
{
A = new A() {x = 3},
B = new B() {x = 4}
},
};
var qA = from item in items
select (object)item.A;
var qB = from item in items
select (object)item.B;
var qAll = qA.Concat(qB);
Is it possible to do this with one query?
If you really want to flatten properties like that, you can feed arrays to SelectMany():
var qAll = items.SelectMany(item => new object[] { item.A, item.B });
You could use a ForEach:
var qAll = new List<object>();
items.ForEach(item => { qAll.Add(item.A); qAll.Add(item.B) });
Related
I saw a method that copies the fields between two different objects but with identical fields.
The question is there a way to copy a list of A to a list of B?
public class A
{
public int X { get; set; }
public int Y { get; set; }
}
public class B
{
public int X { get; set; }
public int Y { get; set; }
}
public Main()
{
B b = new B { X = 10, Y = 2 };
A a = new A();
CopyProperties(b, a); // it works
List<B> listB = new List<B> { new B { X = 5, Y = 4 } };
List<A> listA = new List<A>();
CopyProperties(listB, listA); // it doesn't work
}
public static Target CopyProperties<Source, Target>(Source source, Target target)
{
foreach (var sProp in source.GetType().GetProperties())
{
bool isMatched = target.GetType().GetProperties().Any(tProp => tProp.Name == sProp.Name && tProp.GetType() == sProp.GetType() && tProp.CanWrite);
if (isMatched)
{
var value = sProp.GetValue(source);
PropertyInfo propertyInfo = target.GetType().GetProperty(sProp.Name);
propertyInfo.SetValue(target, value);
}
}
return target;
}
You could use LINQ:
List<B> listB = new List<B> { new B { X = 5, Y = 4 } };
List<A> listA = listB.Select(x => CopyProperties(x, new A()).ToList();
This iterates listB, creates a new instance of A for each entry of the list, copies the entry of listB into the newly created instance and stores them in a new list.
Online demo: https://dotnetfiddle.net/rfL3fn
I have following objects
class A
{
public List<B> listB { get; set; }
}
class B
{
public int id { get; set; }
}
and in my application I have a scenario like below..
public void main()
{
var lstA = new List<A>();
var lstA = new List<A>();
var a1 = new A();
a1.listB = new List<B>
{
new B() { id = 1 },
new B() { id = 2 }
};
lstA.Add(a1);
a1 = new A();
a1.listB = new List<B>
{
new B() { id = 3 },
new B() { id = 4 }
};
lstA.Add(a1);
}
And I need to select all id's of B objects from lstA
Here is what I've tried so far
var ids = lst.Select(x=>x.listB.Select(y=>y.id)).ToList();
But It gives me a compilation error.
How can I do this?
You have to use SelectMany which flattens the lists:
var ids = lst.SelectMany(x => x.listB.Select(y => y.id)).ToList();
You are almost there, use SelectMany
var ids = lst.SelectMany(x=>x.listB.Select(y=>y.id)).ToList();
Check your Working Code
Here's what i did and it works perfectly
All i did was make the classes public and when you initialise List<B>, you add new List<B> because even though intellisense doesn't show you any error, when you run the application, you get object not referenced error
class Program
{
static void Main(string[] args)
{
var lstA = new List<A>();
var a1 = new A()
{
listB = new List<B>()
{
new B
{
id = 3
},
new B
{
id = 5
}
}
};
var a2 = new A()
{
listB = new List<B>()
{
new B
{
id = 1
},
new B
{
id = 8
}
}
};
lstA.Add(a1);
lstA.Add(a2);
var ids = lstA.SelectMany(r => r.listB.Select(x => x.id));
foreach (var id in ids)
{
Console.WriteLine(id);
}
Console.ReadKey();
}
}
public class A
{
public List<B> listB { get; set; }
}
public class B
{
public int id { get; set; }
}
try this to ignore duplicate id
var ids = lstA.SelectMany(x => x.listB.Select(y => y.id)).Distinct().ToList();
Continuing from the Q&As dealing with looping through an object's properties (Using LINQ to loop through inner class properties in outer class collection), is it possible to populate a dictionary:
Dictionary<ComplexKey,IEnumerable<double>> answer;
For example,
answer[1,1,1,"MeasurementA"] = {2.0, 2.1}
answer[1,1,1,"MeasurementB"] = {3.0, 3.1}
answer[1,1,1,"MeasurementC"] = {4.0, 4.1}
answer[1,1,2,"MeasurementA"] = {5.0, 5.1}
Given the structure:
class MainClass {
List<Class1> list
}
class Class1 {
// a number of fields including...
int PropertyA { get; set; }
int PropertyB { get; set; }
Dictionary<int, Class2> dict { get; set; }
}
class Class2 {
// a number of fields all of type double...
double MeasurementA { get; set; }
double MeasurementB { get; set; }
double MeasurementC { get; set; }
}
struct ComplexKey {
public int class1PropA;
public int class1PropB;
public int class1DictKey;
public string class2PropName;
}
Given data:
MainClass mainClass = new MainClass();
mainClass.list = new List<Class1>() {
new Class1() {
PropertyA = 1,
PropertyB = 1,
dict = new Dictionary<int,Class2>() {
{ 1, new Class2() { MeasurementA = 2.0, MeasurementB = 3.0, MeasurementC = 4.0 }},
{ 2, new Class2() { MeasurementA = 5.0, MeasurementB = 6.0, MeasurementC = 7.0 }}
}
},
new Class1() {
PropertyA = 1,
PropertyB = 1,
dict = new Dictionary<int,Class2>() {
{ 1, new Class2() { MeasurementA = 2.1, MeasurementB = 3.1, MeasurementC = 4.1 }},
{ 2, new Class2() { MeasurementA = 5.1, MeasurementB = 6.1, MeasurementC = 7.1 }}
}
}
};
Noting in this example, Class1.PropertyA and Class1.PropertyB are consistently set to "1" for brevity. (This is not the case in the real world data set).
I believe populating the Dictionary "answer" would require, grouping the mainclass.list by PropertyA, PropertyB, and dict.Key before accessing the properties and values within dict.Values (i.e. instances of Class2).
I thought LINQ was the answer but have been stuck for many weeks. Any direction would be appreciated.
Thanks & regards
Shannon
Based on the question you linked, it seems you're looking for something like this:
var answer =
typeof(Class2).GetProperties(BindingFlags.Public | BindingFlags.Instance)
.SelectMany(pi => mainClass.list.SelectMany(c1 =>
c1.dict.Select(i => new {
c1.PropertyA,
c1.PropertyB,
i.Key,
i.Value}))
.Select(p => new {
Key = new ComplexKey {
class1PropA = p.PropertyA,
class1PropB = p.PropertyB,
class1DictKey = p.Key,
class2PropName = pi.Name},
Value = (double)pi.GetValue(p.Value)}))
.GroupBy(grp => grp.Key)
.ToDictionary(grp => grp.Key, grp => grp.Select(x => x.Value));
answer is now
Note: If you use a custom type (like ComplexKey) as key in a Dictionary, you should implement a proper GetHashCode and Equals method, since these methods are used to distinguish the keys inside the Dictionary.
I have the class:
public class DataModel {
public int X { get; set; }
public int Y { get; set; }
}
And the following list:
var data = new List<DataModel>();
data = service.GetData();
I need to change the List to a List:
public class NewDataModel {
public int[] XValues { get; set; }
public int[] YValues { get; set; }
}
So XValues are all the X Values in the original list ... The same with YValues.
How can I do this with Linq?
Thank You,
Miguel
You are expected to post your best try.
var newList = new NewDataModel {
XValues = data.Select(dm => dm.X).ToArray(),
YValues = data.Select(dm => dm.Y).ToArray() };
Untested, but something like this:
var newDataModel = new NewDataModel();
newDataModel.XValues = data.Select(d => d.X).ToArray();
newDataModel.YValues = data.Select(d => d.Y).ToArray();
You should use Aggregate to loop it just once:
newDataModel = data.Select((x,i)=>new{ x, i})
.Aggregate(new NewDataModel(){XValues = new int[data.Count],
YValues = new int[data.Count]},
(a,b)=> {
a.XValues[b.i] = b.x.X;
a.YValues[b.i] = b.x.Y;
return a;
});
Consider these two tables:
ClassID Name
1 C1
2 C2
ClassID List<CourseSession>
1 [Object that has value "A"], [Object that has value "B"]
2 [Object that has value "B"], [Object that has value "C"]
When I join these two tables in Linq, I get:
ID Name List
1 C1 [A, B]
2 C2 [A, B]
Wheras I need to expand them:
ID Name List
1 C1 A
1 C1 B
2 C2 A
2 C2 B
Linq code:
var classes = from row in t.AsEnumerable()
select new
{
ClassID = row.Field<Guid>("ClassID"),
ClassName = row.Field<string>("Name"),
};
var classCourses = from row in classes.AsEnumerable()
select new
{
ID = row.ID,
CourseSessionList = GetAllCoursesByID(row.ID).AsEnumerable()
};
//Attempt to join
var expandedClassCourse = from classRow in classes
join ccRow in classCourses
on classRow.ID equals ccRow.ID
into filteredExpandedClasses
select filteredExpandedClasses;
I'm not sure how to achieve this. Any ideas?
Something like (not sure what your model looks like):
context.CouseSessions.Where(cs => /* condition goes here */)
.Select(cs =>
new
{
Name = cs.Name,
Class = cs.Class.Name
});
or
context.Classes.Where(c => /* condition goes here */)
.SelectMany(c => c.Courses)
.Select(cs =>
new
{
Name = cs.Name,
Class = cs.Class.Name
});
I created two models based on assumption. I hope this helps.
class Info
{
public int Id { get; set; }
public string Name { get; set; }
public List<string> List { get; set; }
}
class MyClass
{
public int Id { get; set; }
public string Name { get; set; }
public string s { get; set; }
}
static void Main(string[] args)
{
var infos = new List<Info> { new Info { Id = 1, Name = "c1", List = new List<string> { "A", "B" } }, new Info { Id = 2, Name = "c2", List = new List<string> { "A", "B" } } };
var myClasses = new List<MyClass>();
foreach (var info in infos)
{
myClasses.AddRange(info.List.Select(a => new MyClass { Id = info.Id, Name = info.Name, s = a }));
}
}
(from c in classList
join s in sessionList on c.ClassID equals s.ClassID
select new
{
ID = c.ClassID,
Name = c.Name,
SessionList = s.SessionList
})
.SelectMany(e => e.SessionList.Select(s => new
{
ID = e.ClassID,
Name = e.Name,
Session = s
}))