Flatten collection of key-collection items Linq - c#

How would I go about flattening below structure using Linq?
I'm looking at following result:
Date Ids
1/1/2011 1
1/1/2011 2
1/1/2011 3
1/1/2012 3
etc..
I tried SelectMany, but I'm doing it wrong. Some snippets for this example:
public class DTO
{
public DateTime Date { get; set; }
public List<int> Ids { get; set; }
}
as
var dataSet = new List<DTO>{
new DTO {Date = new DateTime(2011,1,1), Ids = new List<int>{1,2,3} },
new DTO {Date = new DateTime(2012,1,1), Ids = new List<int>{3,4,5} },
new DTO {Date = new DateTime(2013,1,1), Ids = new List<int>{5,6,7} }
};

You're looking to SelectMany
var result = dataSet.SelectMany(x => x.Ids, (dto,id) => {
return new { date = dto.Date,id};
});
Live example: http://rextester.com/GHY67873

The selectmany should be on the lists inside the DTO but while keeping a reference to the date. That's probably the main issue. By using query syntax those constructions are a lot easier:
var res = (from s in dataSet
from id in s.Ids
select new {Date=s.Date, Id = id}).ToList();

And yet another SelectMany variant with an inner Select
var result = dataSet.SelectMany(x => x.Ids.Select(id => new { date = x.Date,id}));

If you still want Ids as list you can do
var newDataSet = from dto in dataSet
from id in dto.Ids
select new DTO { Date = dto.Date, Ids = new List<int> { id }};
If you want a single id property like
public class NewDTO
{
public DateTime Date { get; set; }
public int Id { get; set; }
}
You can do
var newDataSet = from dto in dataSet
from id in dto.Ids
select new NewDTO { Date = dto.Date, Id = id };

Here another solution using SelectMany
var results = dataSet.Select(x => new
{
dto = x.Ids.Select(y => new
{
date = x.Date,
id = y
})
}).SelectMany(x => x.dto).ToList();

Related

Add element to a List when delcaring new object

Is there a way to add elements to a List when doing this:
var Foo = new MyClass() {
PropertyList = MyList,
Id = Id,
}
I would like to add elements to PropertyList. For example would be the same as: MyList.Add()
The problem is that i do not have a list called MyList but i rather have elements that i want to append to PropertyList
Updating code based on comments:
var result1 = await query
.GroupBy(c => new {
c.CommissionId, c.ActivityId
})
.Select(grp => new RegistrationStatisticViewModel() {
CommissionId = grp.Key.CommissionId,
CommissionCode = grp.First().Commission.Code,
CommissionDescription = grp.First().Commission.Description,
MinuteWorked = grp.Sum(c => c.MinuteWorked),
ActivityId = grp.Key.ActivityId,
ActivityCode = grp.First().Activity.Code,
ActivityDescription = grp.First().Activity.Description,
})
.ToListAsync();
var grps = from d in result1
group d by d.CommissionId
into grp
select new RegistrationStatisticViewModel() {
CommissionId = grp.Key,
ActivityList = new List < Activity > {
new Activity {
//ActivityId = grp.Select(d => d.ActivityId),
//Code = grp.Select(d => d.ActivityCode),
//Description = grp.Select(d => d.ActivityDescription),
}
},
CommissionCode = grp.First().CommissionCode,
CommissionDescription = grp.First().CommissionDescription,
MinuteWorked = grp.First().MinuteWorked
};
return grps;
To give context:
forget the result1 is just some data i retrieve from my database
Commission is one class and contains:
CommissionId
Code
Description
Activity is one class and contains:
ActivityId ==> type GUID
Code ==> type string
Description ==> type string
Now the var = grps is a LINQ that gets the data and then instatiates a new object (class) new RegistrationStatisticViewModel()
So the tricky part we were discussing before is when i populate ActivityList with multiple activities.
When populating the list if i use .First() or .Select() i would only get one instance and therfore the list would only have one activity.
It worked when using .ToArray() for example if i replace ActivityList with just the ActivityId of type string (so a new property on RegistrationStatisticViewModel that is not a list anymore):
I can do this ActivityId = grp.Select(d2 => d2.ActivityId).ToArray()
And it will give me an array of all the ActivityId linked to that commissionId
I am sorry if this is confusing but it is for me as well. I would thank you if you could help me. No worries if you can't you have already give me very helpful answers, so i thank you for that!
Based on your remarks, I believe this is what you are trying to achieve:
public class PersonContainer
{
public IList<Person> Persons { get; set; }
}
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
var personContainer = new PersonContainer
{
Persons = new List<Person>
{
new Person
{
Name = "John Doe",
Age = 28,
},
new Person
{
Name = "Jane Doe",
Age = 27,
},
}
};
Here, the Persons property of PersonContainer is initialized and populated with Person elements during instantiation.

LINQ merge multiple lists to one list

Let's assume I have multiple db tables, I'll just represent them as lists for convenience:
EntitySource {
public int Id {get; set;}
public ICollection<Entity_1> table_1 { get; set }
public ICollection<Entity_2> table_2 { get; set }
public ICollection<Entity_3> table_3 { get; set }
}
Entity_1/Entity_2/Entity_3 {
public int Id { get; set; }
public string Name { get; set; }
}
List<Entity_1> table1 = new List<Entity_1>() {new Entity_1{Id = 1, Name = "First"}, new Entity_1{Id = 2, Name = "Second"}
List<Entity_2> table2 = new List<Entity_2>() {new Entity_2{Id = 3, Name = "First"}, new Entity_2{Id = 4, Name = "Second"}
List<Entity_3> table3 = new List<Entity_3>() {new Entity_3{Id = 5, Name = "First"}, new Entity_3{Id = 6, Name = "Second"}
I'm querying against EntitySource which contains references to multiple collections that I want to query against and map to MergedList class that contains two properties, Id of the entity source and one collection containing all merged collections of the EntitySource.
What I want to achieve is query for only id's and map them to single list of integers.
Something like this:
var entities = await entitySource.Queryable()
.Select(e => new MergedList()
{
PrincipalId = e.Id,
CombinedIds = e.table1.Select(e => e.Id)
.Concat(e.table2.Select(e => e.Id)
.Concat(e.table3.Select(e => e.Id)
})
.ToListAsync(cancellationToken);
public class MergedList {
public int PrincipalId {get;set;}
public IEnumerable<int> CombinedIds {get;set;}
}
But apparently the above statement is not working, expression could not be parsed.
Unable to translate a collection subquery in a projection since either
parent or the subquery doesn't project necessary information required
to uniquely identify it and correctly generate results on the client
side.
I'm using Entity Framework Core v6.0
You can combine them on the client side.
var filtered = entitySource.Queryable()
.Where(ent => input.Id == ent.Id);
var rawData = await
filtered.SelectMany(e => e.table1.Select(t => new { e.Id, SubId = t.Id } ))
.Concat(filtered.SelectMany(e => e.table2.Select(t => new { e.Id, SubId = t.Id } ))
.Concat(filtered.SelectMany(e => e.table3.Select(t => new { e.Id, SubId = t.Id } ))
.ToListAsync(cancellationToken);
var entities = rawData.GroupBy(x => x.Id)
.Select(g => new MergedList()
{
PrincipalId = g.Key,
CombinedIds = g.Select(x => x.SubId).ToList()
})
.ToList();

Detect duplicate set of IDs in grouped collection

I have the following classes:
public class ExistingProposal
{
public Guid ProposalIdPK { get; set; }
public int StatusCode { get; set; }
public List<ExistingProposalElement> GoldenRecordElements { get; } = new List<ExistingProposalElement>();
}
public class ExistingProposalElement
{
public Guid ProposalElementIdPK { get; set; }
public Guid ProposalIdFK { get; set; } // foreignkey referencing ExistingProposal.ProposalIdPK
}
This classes are initialized with existing proposals and its elements. The Proposal will have various StatusCode's like "Accepted", "Declined", etc.
While creating new proposals and elements I need to check if there is already a proposal containing the same elements like the new ones
so lets assume the folowing situation:
ExistingProposal ExistingProposalElement
1 1
2
3
2 5
6
7
Now a new proposal needs to be created with elements for example with ID's 5,6 and 7. My check now needs to detect
that there is already a proposal with these ID's.
The new ProposalElement-ID's are contained in a
List<Guid> newElements
How can I detect that there is already a proposal containing the same ProposalElement-IDs like contained in my List "newElements" and a particular "StatusCode"?
I assume that it will be related to the Linq "All" method or similar, but I'm really stuck with it since I am not really advanced with Linq.
Any help will be very appreciated.
I advice to use SelectMany as I understand you correctly.. It is easier than other solutions.
var existingItemsWithStatusCode = existingProposals.SelectMany(s => s.GoldenRecordElements,
(s, p) => new { s.ProposalIdPK, s.StatusCode, p.ProposalElementIdPK })
.Where(w => newProposals.SelectMany(s => s.GoldenRecordElements.Select(t => t.ProposalElementIdPK))
.Contains(w.ProposalElementIdPK)).Select(s=>new
{
s.ProposalElementIdPK,s.StatusCode
}).ToList();
var newItems = existingProposals.SelectMany(s => s.GoldenRecordElements,
(s, p) => new {s.ProposalIdPK, s.StatusCode, p.ProposalElementIdPK})
.Where(w => !newProposals.SelectMany(s => s.GoldenRecordElements.Select(t => t.ProposalElementIdPK))
.Contains(w.ProposalElementIdPK)).ToList();
Here are the dummy classes I setup to test this. I set up the two examples you provided and then a new proposal to check that it already exists.
var existingProposal1 = new ExistingProposal
{
Id = 1,
GoldenRecordElements = new List<ExistingProposalElement>
{
new ExistingProposalElement{ Id = 1 },
new ExistingProposalElement{ Id = 2 },
new ExistingProposalElement{ Id = 3 },
}
};
var existingProposal2 = new ExistingProposal
{
Id = 2,
GoldenRecordElements = new List<ExistingProposalElement>
{
new ExistingProposalElement{ Id = 4 },
new ExistingProposalElement{ Id = 5 },
new ExistingProposalElement{ Id = 6 },
}
};
List<ExistingProposal> existingProposals = new List<ExistingProposal>
{
existingProposal1,
existingProposal2
};
var newElements = new List<int> { 1, 2, 3 };
This code should let you know if it already exists.
var exists = existingProposals.Any(x => x.GoldenRecordElements
.Select(y => y.Id)
.Intersect(newElements).Count() == x.GoldenRecordElements.Count());

Getting List of Model1 with List of Model2 in it LINQ - MVC 4 EntityFramework 5

I have a requirement where I need to get a List of Model1 (List) using Linq, the Model1 have List of Model2 (List) in it and I need to fetch that also. For this I have created a Linq but m getting following error:
LINQ to Entities does not recognize the method
'System.Collections.Generic.List1 [OurCourse]
ToList[OurCourse](System.Collections.Generic.IEnumerable1
[OurCourse])' method, and this method cannot be translated into a
store expression.
Please refer below for detail:
I have two tables Colleges and Courses, with following columns:
College: ID, Name, Contact, City, Address
Cource: ID, CollegeID, Name, Years
My project have two view models for them, as follows:
public class OurCollege
{
public string Name { get; set; }
public string Contact { get; set; }
public List<OurCourse> MyCourses { get; set; }
}
public class OurCourse
{
public string Name { get; set; }
public int NumberOfYears { get; set; }
}
Here the the query query which I have prepared but I am getting the error:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
How about doing like this:
MyCourses = from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}
Your complete query will look now:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = (from crs in cld.Course
select new OurCourse
{
Name = crs.Name,
NumberOfYears = crs.Years
}).ToList()
}).ToList();
Actually LINQ to Entities converts your query into SQL.It doesn't know how to translate ToList() in SQL
An alternate is to change you List<T> to IEnumerable<T> and remove ToList() frim your original code:
public IEnumerable<OurCourse> MyCourses { get; set; }
and in query:
var colleges = db.Colleges
.Select(cld => new OurCollege()
{
Name = cld.Name,
Contact = cld.Contact,
MyCourses = cld.Course
.Select(crs => new OurCourse()
{
Name = crs.Name,
NumberOfYears = crs.Years
})
}).ToList();
For more details visit this Entity Framework ToList() in nested type (LINQ to Entities does not recognize the method)
A foreach loop will work, you can write the query something like this
var colleges = db.Colleges
.Select(x => new OurCollege()
{
CollegeId = x.CollegeId,
Name = x.Name,
Contact = x.Contact
}).ToList();
foreach (var college in colleges)
{
college.MyCourse = db.Course.Where(x => x.CollegeId == college.CollegeId)
.Select(x => new OurCourse()
{
Name = x.Name,
NumberOfYears = x.Years
}).ToList()
}

How to get the second element alone from a list which contains 2 elements in c#.net?

This is my list definition
public class EventsList
{
public int EventID { get; set; }
public string EventName { get; set; }
}
This is C# code
string strCurrentUser = CommonWeb.GetLoginUser();
EventsClass EventObj = new EventsClass();
DataSet ds;
List< EventsList> eventList = new List<EventsList>();
EventsList eventobj = new EventsList();
ds=EventObj.GetEvents(strCurrentUser);
I have a drop down in which it shoould display the EventName alone. How could i achieve this??
Your question isn't clear, but it sounds like it might be as simple as using the indexer of List<T>, which makes accessing an element look like array access:
List<string> values = ...;
string name = values[1]; // Index is 0-based
For a more general IEnumerable<string> you can use the ElementAt extension method:
using System.Linq;
...
IEnumerable<string> values = ...;
string name = values.ElementAt(1);
.Select(i => i.Name);
e.g.
static void Main(string[] args)
{
var records = GetPersonRecords();
var onlyName = records.Select(i => i.Name);
}
private static List<Person> GetPersonRecords()
{
var listPerson = new List<Person>();
listPerson.Add(new Person { Id = 1, Name = "Name1" });
listPerson.Add(new Person { Id = 2, Name = "Name2" });
return listPerson;
}
}
class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
Hope this helps
Power of Linq we can achieve this...
Below example I Retrieve the particular Property alone..
List<Item> oListItem = new List<Item>() {
new Item("CD", "001CD", Enum.GroupTYPE.FAST_MOVING),
new Item("TV", "002CD", Enum.GroupTYPE.FAST_MOVING),
new Item("CD", "001CD", Enum.GroupTYPE.FAST_MOVING),
new Item("LAPTOP", "003CD", Enum.GroupTYPE.FAST_MOVING),
new Item("MOBILE", "004CD", Enum.GroupTYPE.NORMAL),
new Item("CHARGER", "005CD", Enum.GroupTYPE.LEAST_MOVING)
};
Retrieve the Name property alone from the Collection
var Item = from Item oname in oListItem select oname.ItemName;
it can be easily achieved using LINQ. Also, we can modify how we want to distinguish the required item.
var Item = oListItem.Select(x=>new Item()).Skip(1).Take(1);
Skip = How many items we want to skip
Take = How many we want to take

Categories