Joining items in a single List<> - c#

I have a list of custom objects called EntertainmentEvent:
public class EntertainmentEvent
{
public string Title { get; set; }
public string TagLine { get; set; }
public string Overview { get; set; }
public string ThumbnailUrl { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string EventTime { get; set; }
public Reoccurrence Reoccurrence { get; set; }
public string Url { get; set; }
}
I'd like to merge items with the same StartDate together into a single EntertainmentEvent which has a title of the two merged items concatenated together.
So far I have this:
var duplicateDates = allEvents.Join
(
allEvents, x => x.StartDate, y => y.StartDate,
(x, y) => x.Title != y.Title
? new EntertainmentEvent
{
Title = string.Format("{0}, {1}", x.Title, y.Title),
StartDate = x.StartDate
}
: null
)
.Where(x => x != null)
.ToList();
The only problem with this method is that I get duplicated items - for a sinlge date , duplicateDates list will end up with two entries
Entry 1: Startdate = 1/1/2011, Title = "Item One Title, Item Two Title"
Entry 2: Startdate = 1/1/2011, Title = "Item Two Title, Item One Title"
I'm certain there's a better way of coding this but research has come up empty thus far.
Thanks :)

var result = allEvents
.GroupBy(e => e.StartDate)
.Select(gp => new EntertainmentEvent
{
Title = string.Join(", ", gp),
StartDate = gp.Key
});

Have u tried using group by StartDate? Then u can merge all listed items into one

The following will be close, though you'll need to add Null checking and all that.
class Comparer : IEqualityComparer <EntertainmentEvent>
{
public bool Equals( EntertainmentEvent x, EntertainmentEvent y )
{
return x.Startdate == y.Startdate;
}
public int GetHashCode( EntertainmentEvent event )
{
return event.StartDate.GetHashCode();
}
}
var duplicateDates = allEvents.Join
(
allEvents, x => x.StartDate, y => y.StartDate,
(x, y) => x.Title != y.Title
? new EntertainmentEvent
{
Title = string.Format("{0}, {1}", x.Title, y.Title),
StartDate = x.StartDate
}
: null
)
.Where(x => x != null)
.Distinct( new Comparer() )
.ToList();

Related

ViewModel with mixed fields and lists

I have the following ViewModel:
public class DayTaskListViewModel
{
public int Id { get; set; }
public string DateFormatted { get; set; }
public bool HasRegistrations { get; set; }
public bool HasStartedRegistrations { get; set; }
public string ItemName { get; set; }
public string WorkTypeName { get; set; }
public string Description { get; set; }
public string LocationName { get; set; }
public bool IsActive { get; set; }
public string UserName { get; set; }
public string StateStatus { get; set; }
public DateTime TodaysDate { get; set; }
public int WeekNumber { get; set; }
public int YearNumber { get; set; }
public string Msg { get; set; }
public string SignInUserName { get; set; }
public string UnitCode { get; set; }
public IEnumerable<Machinery> machineryList { get; set; }
public IEnumerable<Cleaning> cleaningList { get; set; }
}
In my controller I have this definition of the model to be sent to the view:
var model = (from a in _db.WorkTask.Where(y => y.TaskDate.Date == querytodaysDate.Date && y.IsActive == true && (y.IsPrivateUserName == null || y.IsPrivateUserName == currUserName))
join b in _db.WorkTaskLog.Where(x => (x.UserName == currUserName || x.UserName == null) && x.IsActive == true && x.StateStatusId < 4) on a.Id equals b.WorkTaskId into joinedT
from c in joinedT.DefaultIfEmpty()
select new DayTaskListViewModel
{
Id = a.Id,
DateFormatted = a.DateFormatted,
HasRegistrations = a.HasRegistrations,
HasStartedRegistrations = a.HasStartedRegistrations,
ItemName = a.ItemName,
WorkTypeName = a.WorkTypeName,
Description = a.Description,
IsActive = c.IsActive ? c.IsActive : false,
UserName = c.UserName ?? String.Empty,
StateStatus = c.StateStatus ?? "Klar",
WeekNumber = (int)currWeekNo,
YearNumber = (int)currYearNo,
Msg = "",
TodaysDate = (DateTime)todaysDate,
SignInUserName = currUserName,
LocationName = a.LocationName,
UnitCode = a.UnitCode,
//machineryList = _db.Machinery.ToList(),
//cleaningList = _db.Cleaning.ToList(),
}).ToList();
My problem is the following lines in the definition:
//machineryList = _db.Machinery.ToList(),
//cleaningList = _db.Cleaning.ToList(),
Everything works as expected, but as soon as I enable those 2 lines it breaks with null errors.
VS can compile, but it breaks runtime.
I think I see the problem, but I don't know how to solve it.
I want ALL fields in the ViewModel EXCEPT the 2 mentioned lines to be a list, and then the 2 lines to be separate lists independent of the majority.
I have tried all combinations of moving those lines around, but then VS complains.
An example from another controller is this:
DriveListViewModel model = new DriveListViewModel()
{
Drive = await _db.Drive
.Where(m => m.StatusId == 5 || m.StatusId == 1010 || m.StatusId == 1012)
.Where(m => m.LoadingComplete == null)
.Where(m => !m.UnitCode.Contains(excludeString))
.Include(s => s.DriveStatus)
.Include(d => d.Location)
.Include(f => f.Item)
.GroupBy(m => m.RegistrationNumber)
.Select(m => m.FirstOrDefault())
.OrderBy(m => m.DriverToLoad)
.ToListAsync(),
machineryList = await _db.Machinery.ToListAsync(),
cleaningList = await _db.Cleaning.ToListAsync(),
};
This works perfectly, but the former model definition as more complex, so basically, I need something similar to the latter example separating the 2 lists from the other properties in the ViewModel.
Maybe this is VERY simple - however I'm struggling with it...
Anyone see the solution to this?
I don't think that you model is a list with so many includes.
I guess you really need this ViewModel
public class ViewModel
{
public List<DayTaskListViewModel> DayTaskListViewModelList {get; set;}
public List<Machinery> MachineryList {get; set;}
public List<Cleaning> CleaningList {get; set;}
}
code
var dayTaskListViewModel= (from ....
.....
select new DayTaskListViewModel
{
Id = a.Id,
.......
}).ToList(); // or ToListAsync too?
var model = new ViewModel
{
DayTaskListViewModel = dayTaskListViewModel,
MachineryList = _db.Machinery.ToList(),
CleaningList = _db.Cleaning.ToList()
}
// or if you can or prefer , use async
MachineryList = await _db.Machinery.ToListAsync(),
CleaningList = await _db.Cleaning.ToListAsync(),
Here is a snip of the view:
Top:
#model List<Day.Models.ViewModels.DayTaskListViewModel>
#using Day.Extensions
Looping through all properties:
if (Model.Count > 0)
{
foreach (var item in Model)
{
<input name="description" class="form-input" id="description" type="text" value="#item.Description">
..Several input fields...
}
}
Works fine.
Then I need to include select fields using the machineryList from the ViewModel:
<select name="machinery" asp-items="#Model.machineryList.ToSelectListItem((int)defaultMachinery)"></select>

Linq group result to list of object

I'm trying group a collection of data by it's State but I'm stuck on the correct way to do this:
FileStateInfoDto
public class FileStateInfoDto : EntityDto<int>
{
public string StateName { get; set; }
public int StateNumber { get; set; }
public int FilesByStateCount { get; set; }
}
FileGroupDto
public class FileGroupDto : EntityDto<int>
{
public int CaseId { get; set; }
public string Name { get; set; }
public string ResourceKey { get; set; }
public bool IsFolder { get; set; }
public int SequenceNumber { get; set; }
public IList<FileStateInfoDto> FileStateInfo { get; set; }
public IList<FileGroupDto> FileGroups { get; set; }
public IList<FileInfoDto> Files { get; set; }
}
Here is the code I have:
return await Context.FileGroups
.Include(g => g.Case).Include(g => g.FileGroups).Include(g => g.Files)
.Where(g => g.Id == fileGroupId &&
g.CaseId == caseId &&
g.Case.CaseState != CaseState.Approved &&
g.Case.CaseState != CaseState.Submitted &&
(g.Case.CaseState != CaseState.Draft || g.Case.CreatorUserId == userId))
.OrderBy(g => g.SequenceNumber)
.Select(g => new FileGroup
{
Id = g.Id,
CaseId = g.CaseId,
Name = g.Name,
ResourceKey = g.ResourceKey,
IsFolder = g.IsFolder,
SequenceNumber = g.SequenceNumber,
FileGroups = g.FileGroups,
FileStateInfo = g.Files.GroupBy(f => f.State), <-- My problem
Files = g.Files.Where(f => f.IsActive && f.State != FileApprovalState.Approved).Select(
f => new File
{
Id = f.Id,
CreationTime = f.CreationTime,
CreatorUserId = f.CreatorUserId,
Title = f.Title,
FileName = f.FileName,
URL = f.URL,
Size = f.Size,
KeepOnPortal = f.KeepOnPortal,
CreatorUserName = Context.Users.FirstOrDefault(u => u.Id == (f.CreatorUserId ?? 0)).UserName,
CreatorUserRole = Context.CasePersons.Where(p => p.CaseId == caseId && p.UserId == f.CreatorUserId).Take(1).Select(p => p.CaseRoleType.Title).FirstOrDefault()
}
).ToList()
}).FirstOrDefaultAsync();
I'm trying to figure out how I should write this line FileStateInfo = g.Files.GroupBy(f => f.State) so it will give the expected result as below.
FileStateInfo = [{"StateName":"Approved","StateNumber":1, "FilesByStateCount":22},
{"StateName":"NotApproved","StateNumber":2, "FilesByStateCount":11}]
The State in g.Files.GroupBy(f => f.State) is an enum that contains Approved and NotApproved
StateName = Name of the State.
StateNumber = The Integer assinged.
FilesByStateCount = The files count by this state.
I hope it's possible because I've been trying to make this for a few days now.
I've tried things like this Post

C# how to remove duplicate objects from a list

I want to remove duplicate objects from a list. My code works, but I'm still afraid I'll make a mistake. Especially if the amount of data is larger, this solution doesn't make sense to me. I ask for your comments on my code.
// Print the list with duplicates
PrintList(listWithDuplicates);
// This code is not working
noDuplicates = listWithDuplicates.Distinct().ToList();
// This code is working but I am not sure if it is good practice
// especially if I have a large number of data
noDuplicates = listWithDuplicates
.GroupBy(x => x.input1)
.Select(x => x.First())
.GroupBy(x => x.input2)
.Select(x => x.First())
.GroupBy(x => x.output1)
.Select(x => x.First())
.GroupBy(x => x.output2)
.Select(x => x.First())
.ToList();
// Print the list without duplicates
PrintList(noDuplicates);
Console.ReadLine();
}
class Data
{
public string input1 { get; set; }
public string input2 { get; set; }
public string output1 { get; set; }
public string output2 { get; set; }
}
You tried to use .Distinct() without further telling it how to compare instances of your Data-class.
Therefore you could create a Comparer class which you'd then pass to .Distinct() as a parameter:
public class Data
{
public string input1 { get; set; }
public string input2 { get; set; }
public string output1 { get; set; }
public string output2 { get; set; }
}
public class DataComparer : EqualityComparer<Data>
{
public override bool Equals(Data x, Data y)
{
if (x.input1 == y.input1 &&
x.input2 == y.input2 &&
x.output1 == y.output1 &&
x.output2 == y.output2)
{
return true;
}
return false;
}
public override int GetHashCode(Data obj)
{
return $"{obj.input1}{obj.input2}{obj.output1}{obj.output2}".GetHashCode();
}
}
Here is an example:
var dataList = new List<Data>()
{
new Data(){ input1="A", input2="B", output1="B", output2="A"},
new Data(){ input1="A", input2="B", output1="B", output2="A"},
new Data(){ input1="C", input2="D", output1="D", output2="C"},
new Data(){ input1="C", input2="D", output1="D", output2="C"}
};
dataList = dataList.Distinct(new DataComparer()).ToList();
A friend showed me how to do the job without a comparer and without override methods.
noDuplicates = listWithDuplicates.GroupBy(x => new { x.input1, x.input2, x.output1, x.output2 }).Select(y => y.First()).ToList();

Updating a list based on another list

I have two lists of users.
In the first the users have the following fields - fname,lname, UserDetailsId,FocusStart,FocusEnd,isActive
In the second list the users have - fname, lname, UserDetailsId,totalTime, FocusStart, FocusEnd.
What I am aiming to do is : when the value isActive from the first list equals to 'true' and the userDetailsId equeals UserDetailsId from the second list I want the FocusStart and FocusEnd in the second list to be equals to the values of the matched element in the first list.
Any tips on how to achieve this?
Here is how I get the first list :
var list = listWRUD.
Join(db.UsersDetails,
o => o.UserDetailsId, od => od.identtyUserId,
(o, od) => new
{
fname = od.FirstName,
lname = od.LastName,
UserDetailsId = o.UserDetailsId,
FocusStart = o.FocusStart,
FocusEnd = o.FocusEnd,
isActive = o.isActive
}).ToList();
var a = from x in list
group x by new { x.fname, x.lname, x.UserDetailsId } into g
select new RolesUsersViewModel(g.Key.UserDetailsId, g.Key.fname, g.Key.lname, TimeSpan.FromMilliseconds(g.Sum(x => (x.FocusEnd - x.FocusStart).TotalMilliseconds)));
And here is the second one :
List<RolesUsersViewModel> list_users = a.ToList<RolesUsersViewModel>();
What i've got so far is :
var allActive = list.Where(item => item.isActive == true);
foreach (var p in list_users.Join(allActive, item => item.userId, item => item.UserDetailsId, (x, y) => new { L2 = x, L1 = y }))
{
p.L2.FocusStart = p.L1.FocusStart;
p.L2.FocusEnd = p.L1.FocusEnd;
}
Sadly, this code seems to give me some random results. A date is set to the records in the second list even if there are no records with isActive==true in the first.
The ViewModel :
public class RolesUsersViewModel
{
public RolesUsersViewModel(string userDetailsId, string FirstName, string LastName, TimeSpan totalex)
{
userId = userDetailsId;
fname = FirstName;
lname = LastName;
total = totalex;
}
public RolesUsersViewModel(DateTime focusStart, DateTime focusEnd)//
{
FocusStart = focusStart;
FocusEnd = focusEnd;
}
public string userId { get; set; }
public string fname { get; set; }
public string lname { get; set; }
public TimeSpan total { get; set; }
public DateTime FocusStart { get; set; }//
public DateTime FocusEnd { get; set; }//
}
foreach (var p in list_users)
{
// Get all the items that have matching UserDetailsId
var targets = allActive.Where(x => x.UserDetailsId == p.UserDetailsId);
// Now assign the properties
// my assumption is that the above query should return
// a single record. If my assumption is true then use
// Single or SingleOrDefault and then you do not need
// the loop below but just a simple assignment
foreach(var thisTarget in targets)
{
p.FocusStart = thisTarget.FocusStart;
p.Focused = thisTarget.FocusEnd;
}
}

How to flatten collection using SelectMany

I have the following entities (I18N is a localized entity):
public class Post {
public Int32 Id { get; set; }
public Boolean IsPublished { get; set; }
public List<PostI18N> PostsI18N { get; set; }
public List<Tag> Tags { get; set; }
public Author { get; set; }
}
public class Tag {
public List<TagI18N> TagsI18N { get; set; }
}
public class Author {
public Int32 Id { get; set; }
public String Name { get; set; }
}
public class PostI18N {
public Int32 Id { get; set; }
public String Text { get; set; }
public String Title { get; set; }
}
public class TagI18N {
public Int32 Id { get; set; }
public String Name { get; set; }
}
I need to get all information of 4 posts so I tried to flatten the query:
var posts = await _context
.Posts
.SelectMany(x => x.PostsI18N, (Post, PostI18N) =>
new { Post, PostI18N, Post.Tags, Post.Author })
.Where(x => x.PostI18N.Language == "en")
.Select(x => new PostDTO {
Id = x.Post.Id,
Title = x.PostI18N.Title,
Text = x.PostI18N.Text,
AuthorName = x.Author.Name
TagsNames = // Names taken from x.Tags.TagsI18N where TagsI18N
// language is "en" ... So, for each tag look the
// one Tag.TagI18N which Tag.TagI18N.Language = "en"
// and get Tag.TagI18N.Name
})
.Take(4)
.ToListAsync();
PROBLEM:
The problem is that I also need the TagsI18N flatten so I can take their names for English language ...
It this possible with SelectMany? How should I do this?
Try it in query syntax instead:
var posts = await (
from p in _context.Posts
from pn in p.PostsI18N
where pn.Language == "en"
select new PostDTO {
Id = p.Id,
Title = pn.Title,
Text = pn.Text,
AuthorName = p.Author.Name,
TagsNames = from t in p.Tags
from tn in t.TagsI18N
where tn.Language == "en"
select tn.Name
}).Take(4).ToListAsync();
The SelectMany syntax should work as well, but it gets a bit "nested":
var posts = await _context
.Posts
.SelectMany(x => x.PostsI18N, (Post, PostI18N) =>
new { Post, PostI18N, Post.Tags, Post.Author })
.Where(x => x.PostI18N.Language == "en")
.Select(x => new PostDTO {
Id = x.Post.Id,
Title = x.PostI18N.Title,
Text = x.PostI18N.Text,
AuthorName = x.Author.Name
TagsNames =
x.Tags.SelectMany(t => t.TagsI18N, (Tag, TagI18N) =>
new { Tag, TagI18N })
.Where(t => t.TagI18N.Language == "en")
.Select(t => t.TagI18N.Name)
})
.Take(4)
.ToListAsync();

Categories