How to order the entities by their TimeSpan properties? - c#

I'm working on an Asp.Net Core project and I need to sort my classes Course.cs by their total time.
I have an entity named "Course.cs" which has one-to-many relation with the entity Episode.cs so that each Course.cs has many Episode.cs inside itself.
My Episode.cs is:
public class Episode
{
[Key]
public int EpisodeId { get; set; }
[Required]
public int CourseId { get; set; }
[Display(Name = "عنوان اپیزود")]
[Required(ErrorMessage ="لطفا {0} را وارد کنید")]
[MaxLength(ErrorMessage ="{0} نمیتواند بیش تر از {1}کاراکتر باشد")]
public string EpisodeTitle { get; set; }
public string EpisodeFileName { get; set; }
[Display(Name = "مدت زمان اپیزود")]
[Required(ErrorMessage = "لطفا {0} را وارد کنید")]
public TimeSpan? EpisodeTimeLength { get; set; }
[Display(Name = "رایگان")]
[Required(ErrorMessage = "لطفا {0} را وارد کنید")]
public bool IsFree { get; set; }
public DateTime CreateDate { get; set; }
#region Navigation Properties
[ForeignKey("CourseId")]
public Course.Courses.Course Course { get; set; }
#endregion
}
As you can see above, I have a property named public TimeSpan? EpisodeTimeLength { get; set; } and want to sort my Course.css depending on this property.
I tried this way:
_dbContext.Courses.Include(c => c.Episodes).AsEnumerable().OrderByDescending(c => c.Episodes.Select(t => t.EpisodeTimeLength));
and then tried this way:
_dbContext.Courses.OrderByDescending(t => t.Episodes.Sum(s => s.EpisodeTimeLength ?? TimeSpan.Zero).Ticks));
but they are not compiled!
Would anybody help?

You can do it easily by summing timespans' total milliseconds:
var ordered = courses
.Include(x => x.Episodes)
.ToList()
.OrderByDescending(x => x.Episodes.Sum(e => e.Length?.TotalMilliseconds ?? 0));
Full sample code below:
public class Program
{
static void Main(string[] args)
{
var courses = new Course[]
{
new Course()
{
Name = "Course 1",
Episodes = new List<Episode>()
{
new Episode(){ Length = null },
new Episode(){ Length = TimeSpan.FromSeconds(30) },
}
},
new Course()
{
Name = "Course 2",
Episodes = new List<Episode>()
{
new Episode(){ Length = TimeSpan.FromSeconds(5) },
new Episode(){ Length = TimeSpan.FromSeconds(6) },
new Episode(){ Length = TimeSpan.FromSeconds(6) },
}
},
new Course()
{
Name = "Course 3",
Episodes = new List<Episode>()
{
new Episode(){ Length = TimeSpan.FromSeconds(5) },
new Episode(){ Length = TimeSpan.FromSeconds(6) },
new Episode(){ Length = TimeSpan.FromSeconds(2) },
new Episode(){ Length = TimeSpan.FromSeconds(7) },
}
},
};
var ordered = courses
.Include(x => x.Episodes)
.ToList()
.OrderByDescending(x => x.Episodes.Sum(e => e.Length?.TotalMilliseconds ?? 0));
foreach (var course in ordered) Console.WriteLine(course.Name);
}
}
public class Course
{
public string Name { get; set; }
public IEnumerable<Episode> Episodes { get; set; }
}
public class Episode
{
public TimeSpan? Length { get; set; }
}

Related

Linq List problems with GroupBy and Count

Given the data below, I am trying to write a LINQ statement that will group by ListOfAdmin by id so it will only display one user. I am also checking that the count is not 0. The record one has no ListOfAdmin so that Count==0 should return null but it is displaying records for ListOfAdmin . Also
once I add //.GroupBy(f => f.id) I get the error Cannot implicitly convert type 'System.Collections.Generic.List
All of the other section is working fine.
Once I add ListOfSubscription with no record for ListOfAdmin I get the error
LINQPad Example
class Subscription
{
public int SubscriptionId { get; set; }
public int ParentProductId { get; set; }
public string ParentProductName { get; set; }
public string ChildProductName { get; set; }
public int ChildProductId { get; set; }
public int GroupId { get; set; }
public DateTime? EndDate { get; set; }
public List<admin> ListOfAdmin { get; set; }
}
class SubscriptionViewModel
{
public int SubscriptionId { get; set; }
public int ParentProductId { get; set; }
public string ParentProductName { get; set; }
public string SubscriptionIds { get; set; }
public int GroupId { get; set; }
public List<SubscriptionChildViewModel> ListOfSubscriptionChild { get; set; }
public List<AdminViewModel> ListOfAdmin { get; set; }
}
class SubscriptionChildViewModel
{
public string ChildProductName { get; set; }
public int ChildProductId { get; set; }
}
class AdminViewModel
{
public int id { get; set; }
public string name { get; set; }
}
class admin
{
public int id { get; set; }
public string name { get; set; }
}
void Main()
{
List<Subscription> ListOfSubscription = new List<Subscription>();
List<admin> ListOfAdmin = new List<admin>();
ListOfSubscription.Add(new Subscription() { SubscriptionId = 1, ParentProductId = 4, ChildProductId = 4, ParentProductName = "Product 1", ChildProductName = "Product 1", GroupId = 362,ListOfAdmin= ListOfAdmin });
ListOfAdmin.Clear();
ListOfAdmin.Add(new admin() { id = 1, name= "Mike"});
ListOfAdmin.Add(new admin() { id = 2, name = "Bill" });
ListOfSubscription.Add(new Subscription() { SubscriptionId = 2, ParentProductId = 114, ChildProductId = 1, ParentProductName = "Product 2", ChildProductName = "Product 3", GroupId = 1, ListOfAdmin= ListOfAdmin });
ListOfSubscription.Add(new Subscription() { SubscriptionId = 3, ParentProductId = 114, ChildProductId = 2, ParentProductName = "Product 2", ChildProductName = "Product 4", GroupId = 1, ListOfAdmin = ListOfAdmin });
var groupedSubscriptions = ListOfSubscription.GroupBy(u => u.GroupId);
var result = groupedSubscriptions.Select(grp1 => new
{
GroupId = grp1.Key,
Subscriptions = grp1.GroupBy(subscr => new
{
subscr.ParentProductId,
subscr.ParentProductName,
//subscr.ListOfAdmin
})
.Select(grp2 => new SubscriptionViewModel
{
GroupId = grp1.Key,
ParentProductId = grp2.Key.ParentProductId,
ParentProductName = grp2.Key.ParentProductName,
SubscriptionIds = string.Join(",", grp2.Select(y => y.SubscriptionId)),
ListOfSubscriptionChild = grp2
.Where(subsc => subsc.ChildProductId != grp2.Key.ParentProductId)
.Select(subsc => new SubscriptionChildViewModel
{
ChildProductId = subsc.ChildProductId,
ChildProductName = subsc.ChildProductName
})
.ToList(),
ListOfAdmin = (grp2.SelectMany(y => y.ListOfAdmin).Count()) == 0 ? null : grp2.SelectMany(y => y.ListOfAdmin)
.Select(a => new AdminViewModel
{
id = a.id,
name = a.name
})
//.GroupBy(f => f.id)
.ToList(),
})
});
var x = result.SelectMany((s => s.Subscriptions));
Console.Write(x);
}
Updated: using List<IGrouping<int, AdminViewModel>> I am getting dups.
Let's take a look at the exact error: Cannot implicitly convert type 'System.Collections.Generic.List<System.Linq.IGrouping<int,Program.AdminViewModel>>' to 'System.Collections.Generic.List<Program.AdminViewModel>'.
It is pretty clear: you are trying to assign a value (of type List<IGrouping<int,Program.AdminViewModel>>) that is not of the expected type (List<Program.AdminViewModel>).
You can remedy this by changing SubscriptionViewModel, so that ListOfAdmin will be List<IGrouping<int, AdminViewModel>>, as #itectori suggested.
However, I don't think that's what you're looking for. If I understand correctly, ListOfAdmin should contain, well, the admins. If that is the case, simply select the first item of every IGrouping, like this:
ListOfAdmin = (grp2.SelectMany(y => y.ListOfAdmin).Count()) == 0 ? null : grp2.SelectMany(y => y.ListOfAdmin)
.Select(a => new AdminViewModel
{
id = a.id,
name = a.name
})
.GroupBy(f => f.id)
.Select(g => g.First())
.ToList(),
According to the documentation, GrouBy() returns an object of type IEnumerable<IGrouping<TKey,TElement>>.
In your case,
(grp2.SelectMany(y => y.ListOfAdmin).Count()) == 0 ? null : grp2.SelectMany(y => y.ListOfAdmin)
.Select(a => new AdminViewModel
{
id = a.id,
name = a.name
})
.GroupBy(f => f.id)
.ToList()
return an object of type List<IGrouping<int, AdminViewModel>>
In order for your code to work, you just need to change the type of the SubscriptionViewModel.ListOfAdmin attribute.
class SubscriptionViewModel
{
[...]
public List<IGrouping<int, AdminViewModel>> ListOfAdmin { get; set; }
}

LINQ. Loading related elements

I have an entity Contracts, ListKindWorks and KindWorks.
public partial class Contracts
{
public Contracts()
{
ListKindWorks = new HashSet<ListKindWorks>();
}
public int Id { get; set; }
...
public virtual ICollection<ListKindWorks> ListKindWorks { get; set; }
}
public partial class ListKindWorks
{
public int IdContract { get; set; }
public int IdKindWork { get; set; }
public virtual Contracts IdContractNavigation { get; set; }
public virtual KindWorks IdKindWorkNavigation { get; set; }
}
public partial class KindWorks
{
public KindWorks()
{
ListKindWorks = new HashSet<ListKindWorks>();
}
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<ListKindWorks> ListKindWorks { get; set; }
}
I want to load related elements. Something like this pseudocode:
source = model.Contracts
.Select(c => new MyType
{
IdContract = c.Id,
KindWork = new List<Item>
{ Id = KindWorks.Id, Value = KindWorks.Title }
// or
// KindWork = c.ListKindWorks
// .Where(x => x.IdContract == c.Id)
// .Select(y => new Item
// { Id = y.IdKindWork, Value = y.IdKindWorkNavigation.Title })
...
})
.ToList();
public class Item
{
public int Id { get; set; }
public string Value { get; set; }
}
Can I load List<Item> for each Contracts?
If I understand what you are looking for, I create a List for each contract in a dictionary. And here is my result:
var contracts = new List<Contracts>
{
new Contracts { Id = 1 },
new Contracts { Id = 2 },
new Contracts { Id = 3 },
};
var listKindWorks = new List<ListKindWorks>
{
new ListKindWorks { IdContract = 1, IdKindWork = 1 },
new ListKindWorks { IdContract = 1, IdKindWork = 2 },
new ListKindWorks { IdContract = 2, IdKindWork = 2 },
new ListKindWorks { IdContract = 2, IdKindWork = 3 }
};
var kindWorks = new List<KindWorks>
{
new KindWorks { Id = 1, Title = "Title 1" },
new KindWorks { Id = 2, Title = "Title 2" },
new KindWorks { Id = 3, Title = "Title 3" },
};
Dictionary<Contracts, List<Item>> myDic = contracts.Select(
contract => contract).ToDictionary(
contract => contract,
contract => listKindWorks.Where(
listKindWork => listKindWork.IdContract.Equals(contract.Id))
.Select(listKindWork => new Item
{
Id = kindWorks.FirstOrDefault(kindWork => kindWork.Id.Equals(listKindWork.IdKindWork))?.Id?? listKindWork.IdKindWork,
Value = kindWorks.FirstOrDefault(kindWork => kindWork.Id.Equals(listKindWork.IdKindWork))?.Title?? "KindWork not found"
}).ToList());
I obtain this for my test :
Contract1 : Title1, Title2
Contract2 : Title2, Title3
Contract3 : Nothing
IEnumerable<Item> KindWork = c.ListKindWorks
.Select(y => new Item
{
Id = y.IdKindWork,
Value = y.IdKindWorkNavigation.Title
})
IEnumerable<Item> Subject = c.ListSubjects
.Select(y => new Item
{
Id = y.IdSubject,
Value = y.IdSubjectNavigation.Title
})

handle empty Json Array from object

I have a json object that returns two empty arrays
"subQuestions": [] and "answers": []
I have created classes for the object, but I cannot get it to work,
this is what I have so far.
Object
"questions": [
{
"questionId": "1",
"question": "Ipsum",
"helpText": null,
"questionType": "MultipleChoice",
"answerChoices": [
{
"answerChoiceId": "b2b-2.01-answer1",
"value": "Lorem",
"subQuestions": []
}
],
"answers": []
}
Classes
public class AnswerChoice
{
public string answerChoiceId { get; set; }
public string value { get; set; }
public List<object> subQuestions { get; set; }
}
public class Question
{
public string questionId { get; set; }
public string question { get; set; }
public object helpText { get; set; }
public string questionType { get; set; }
public List<AnswerChoice> answerChoices { get; set; }
public List<object> answers { get; set; }
}
public class ObjectRoot
{
public string productId { get; set; }
public List<Question> questions { get; set; }
}
var jsonBody = new ObjectRoot()
{
productId = productId,
questions = new[]
{
new Question() {
questionId = "b2b-2.01",
question ="Vad är syftet med ert engagemang hos oss?",
helpText = null,
questionType = "MultiChoise",
answerChoices = new []{
new AnswerChoice{
answerChoiceId = "",
value = "",
**HERE is what it gets tricky for me**
}
}
}
}
};
The tricky part for me is after value = "", and the subQuestion object needs to be added, have tried multiple ways but no luck.
In your classes all collections is List not array. So to make it work you need to call ToList extension method. And for empty collections just call List constructor
var jsonBody = new ObjectRoot()
{
productId = "productId",
questions = new[]
{
new Question() {
questionId = "b2b-2.01",
question ="Vad är syftet med ert engagemang hos oss?",
helpText = null,
questionType = "MultiChoise",
answerChoices = new []{
new AnswerChoice{
answerChoiceId = "",
value = "",
subQuestions = new List<object>() // empty collection
}
}.ToList(),
answers = new List<object>()
}
}.ToList()
};

How to get data in string array or JSON format using linq to entities?

I have an ASP.Net MVC5 site and using EF 6.0
One to Many relationship
Here are my models
public class Singer
{
[Key]
public int SingerID { get; set; }
public string SingerName { get; set; }
public virtual List<Album> Albums { get; set; }
}
public class Album
{
[Key]
public int AlbumID { get; set; }
public string AlbumName { get; set; }
public string AlbumDate { get; set; }
[ForeignKey("Singer")]
public int SingerID { get; set; }
public virtual Singer Singer { get; set; }
}
Now my Linq is as below
public IEnumerable<T> GetAlbums()
{
using (dbContext db = new dbContext())
{
IQueryable<T> query = (from c in db.Albums
group c.AlbumId by c.SingerId into albums
select new AlbumMapper()
{
AlbumID = albums.Key,
Total = albums.Count()
})
}
}
In the current scenario I get all the albums grouped by albumId and the count of the albums.
But my need is to form JSON string as below
[
{
"SingerID":1,
"Albums":[
{
"AlbumName":"This is Album 1",
"AlbumDate":"Dec 30,2015"
},
{
"AlbumName":"This is Album 2",
"AlbumDate":"Dec 30 2015"
}
]
},
{
"SingerID":2,
"Albums":[
{
"AlbumName":"This is Album 1",
"AlbumDate":"Dec 30,2015"
},
{
"AlbumName":"This is Album 2",
"AlbumDate":"Dec 30 2015"
}
]
}
]
Adding Mapper Classes
public class AlbumDetails
{
public DateTIme AlbumDate
public string AlbumName
}
public class AlbumMapper
{
public int AlbumID
public IEnumerable<AlbumDetails> Albums
}
Just put all the Singers into a list and serialize it using Json.NET (http://www.newtonsoft.com/json)
If you want to leave out SingerName be sure to add a [JsonIgnore] data attribute to the property
Then you want just this, combination of GroupBy with Select using anonymous object.
Using lambda
public IEnumerable<AlbumMapper> GetAlbums()
{
using(dbContext db = new dbContext())
{
return db.Albums.GroupBy(a => a.SingerID)
.Select(g => new AlbumMapper
{
SingerID = g.Key,
Albums = g.Select(a => new AlbumDetails { AlbumName = a.AlbumName, AlbumDate = a.AlbumDate })
});
}
}
You have to modify your map classes to fit this:
public class AlbumDetails
{
public DateTime AlbumDate { get; set; }
public string AlbumName { get; set; }
}
public class AlbumMapper
{
public int SingerID { get; set; }
public IEnumerable<AlbumDetails> Albums { get; set; }
}
Using linq syntax
public IEnumerable<T> GetAlbums()
{
using(dbContext db = new dbContext())
{
return from a in db.Albums
group a by a.SingerID into albums
select new AlbumMapper
{
SingerID = albums.Key,
Albums = albums.Select(album => new AlbumDetails { AlbumName = album.AlbumName, AlbumDate = album.AlbumDate })
};
}
}
With this sample data:
var albums = new List<Album>();
var singer = new Singer(1, "Singer 1");
var singer2 = new Singer(2, "Singer 2");
albums.Add(new Album(1,"This is Album 1", "Dec 30,2015", singer));
albums.Add(new Album(2,"This is Album 2", "Dec 30,2015", singer));
albums.Add(new Album(1,"This is Album 1", "Dec 30,2015", singer2));
albums.Add(new Album(2,"This is Album 2", "Dec 30,2015", singer2));
The result of this
albums.GroupBy(a => a.SingerID)
.Select(g => new
{
SingerID = g.Key,
Albums = g.Select(a => new { a.AlbumName, a.AlbumDate })
})
Is
[
{
"SingerID": 1,
"Albums": [
{
"AlbumName": "This is Album 1",
"AlbumDate": "Dec 30,2015"
},
{
"AlbumName": "This is Album 2",
"AlbumDate": "Dec 30,2015"
}
]
},
{
"SingerID": 2,
"Albums": [
{
"AlbumName": "This is Album 1",
"AlbumDate": "Dec 30,2015"
},
{
"AlbumName": "This is Album 2",
"AlbumDate": "Dec 30,2015"
}
]
}
]

Linq Group By statement on a Dictionary containing a list

I have a dictionary defined as
var dataDict = new Dictionary<String, List<RICData>>();
with the RICData class defined as
class RICData
{
public string pubdate { get; set; }
public string settle { get; set; }
public int colorder { get; set; }
}
The following illustrates the data the dictionary dataDict contains -
"TEST1", ("12/01/2015, 100.1, 1", "12/02/2015, 200.1, 2", "12/03/2015, 300.4, 3")
"TEST2", ("12/01/2015, 150.1, 6", "12/02/2015, 200.1, 7")
"TEST3", ("12/01/2015, 250.1, 4", "12/02/2015, 400, 5")
What I would like to do is group the data by date and order by colorder and retun something simlar to what is below
"12/01/2015", ("TEST1, 100.1, 1", "TEST3, 250.1, 4", "TEST2, 150.1, 6")
"12/02/2015", ("TEST1, 200.1, 2", "TEST3, 400, 5", "TEST2, 200.1, 7"
"12/03/2015", ("TEST1, 300.4, 3")
Here's some sample code. I guess I'm not sure how to group this data
var dataDict = new Dictionary<String, List<RICData>>();
var rdList = new List<RICData>();
rdList.Add(new RICData{pubdate = "12/01/2015", settle = "100.1", colorder = 1});
rdList.Add(new RICData{pubdate = "12/02/2015", settle = "110.1", colorder = 2});
rdList.Add(new RICData { pubdate = "12/03/2015", settle = "120.1", colorder = 3 });
dataDict.Add("TEST1", rdList);
var rdList1 = new List<RICData>();
rdList1.Add(new RICData { pubdate = "12/01/2015", settle = "140.1", colorder = 6 });
rdList1.Add(new RICData { pubdate = "12/02/2015", settle = "100.1", colorder = 7 });
dataDict.Add("TEST2", rdList1);
var rdList2 = new List<RICData>();
rdList2.Add(new RICData { pubdate = "12/01/2015", settle = "240.1", colorder = 4 });
rdList2.Add(new RICData { pubdate = "12/02/2015", settle = "200.1", colorder = 5 });
dataDict.Add("TEST3", rdList2);
//?????
var resultGrp = dataDict.GroupBy(p => p.Value.Select(x => x.pubdate));
public class RICData
{
public string PubDate { get; set; }
public string Settle { get; set; }
public int ColorDer { get; set; }
}
public class NewRICData
{
public string Label { get; set; }
public string Settle { get; set; }
public int Colorder { get; set; }
}
var oldDict = new Dictionary<string, List<RICData>>();
var newDict = oldDict.SelectMany(pair => pair.Value.Select(data => new
{
PubDate = DateTime.Parse(data.PubDate),
NewRICData = new NewRICData
{
Label = pair.Key,
Settle = data.Settle,
ColorDer = data.ColorDer
}
}))
.GroupBy(x => x.PubDate.Date)
.ToDictionary(group => group.Key.ToString("d"),
group => group.Select(x => x.NewRICData)
.OrderBy(x => x.ColorDer));

Categories