Reputation: 33
I have been trying to get the title, description, start and end date in the periods array based on the time given, I have the following JSON data (the variable name of the following is _json):
Update:
What I want to do is to get the title, description, start and enddate in the periods json array, the condition of it will be based on the start and enddate in the periods json array.
[
{
"ID": "1",
"Title": "First Title",
"Description": "First Description",
"Periods": [
{
"StartDate": "2017-04-23 15:30",
"EndDate": "2017-04-23 15:40"
},
{
"StartDate": "2017-04-23 15:42",
"EndDate": "2017-04-23 15:45"
},
{
"StartDate": "2017-04-23 15:47",
"EndDate": "2017-04-23 15:50"
}
]
},
{
"ID" : "2",
"Title": "Second Title",
"Description": "Second Description",
"Periods": [
{
"StartDate": "2017-04-23 15:52",
"EndDate": "2017-04-23 15:55"
},
{
"StartDate": "2017-04-23 15:57",
"EndDate": "2017-04-23 16:00"
},
{
"StartDate": "2017-04-23 16:02",
"EndDate": "2017-04-23 16:05"
}
]
}
]
And here is my code:
public sealed class Information
{
public List<Period> Periods;
public string Title;
public string Description;
public int ID;
}
public sealed class Period
{
public DateTime StartDate;
public DateTime EndDate;
}
var informations = JsonConvert.DeserializeObject<List<Information>>(_json);
var information = GetData(informations, DateTime.Now);
private Information GetData(List<Information> informations, DateTime dateToCheck)
{
return informations.Select(x =>
{
x.Periods = x.Periods
.TakeWhile(y => dateToCheck >= y.StartDate
&& dateToCheck < y.EndDate)
.ToList();
if (x.Periods.Count > 0)
return x;
else
return null;
}).FirstOrDefault();
}
However, it only take the first array (ID: 1) of the JSON data and the first array of the Periods inside the JSON data, even the DateTime.Now is more than 2017-04-23 15:40 or it is 2017-04-23 15:52, it will return null.
Do I filtering not correct for the LINQ or in the between time condition?
Your answer much appreciated.
Second Update:
The expected output will be like:
Have the JSON data, which where the between date time is falling into, for example: between date time is falling into "ID: 2", and "Periods: 2017-04-23 15:57", then it will only return JSON of ID no 2 and falling Periods (in this case 2017-04-23 15:57 as StartDate, and 2017-04-23 16:00 as EndDate), while the rest of the Periods is removed. So it will be like this (It is based on the Periods, which where the date time is falling to, and it will determine which data need to get).
"ID" : "2",
"Title": "Second Title",
"Description": "Second Description",
"Periods": [ {
"StartDate": "2017-04-23 15:57",
"EndDate": "2017-04-23 16:00" }
Thanks
Upvotes: 3
Views: 752
Reputation: 53958
I think that instead of TakeWhile
you need a Where
:
x.Periods = x.Periods
.Where(y => dateToCheck >= y.StartDate
&& dateToCheck < y.EndDate)
.ToList();
Keep in mind that TakeWhile
stops when the condition is false. If you need a more formal explanation, you can find here that:
The TakeWhile(IEnumerable, Func) method tests each element of source by using predicate and yields the element if the result is true. Enumeration stops when the predicate function returns false for an element or when source contains no more elements.
Update
private Information GetFirstMatchingInformationByPeriod(
List<Information> informations
, DateTime dateToCheck)
{
// Find the first information that match your search criteria
var information = informations.FirstOrDefault(information
=> information.Periods
.Any(period
=> period.dateToCheck >= period.StartDate
&& period.dateToCheck < period.EndDate);
// If not any found, return null.
if(information == null) return null;
var firstMatchingPeriod = information.Periods
.First(period => dateToCheck >= period.StartDate
&& dateToCheck < period.EndDate);
// Information found.
// Change it's periods with a List containin only the first matching period.
information.Periods = new List<Period>{ firstMatchingPeriod } ;
return information;
}
Upvotes: 1
Reputation: 8462
There is a much more readable way to write your query (that works):
private static Information GetData(List<Information> informations, DateTime dateToCheck)
{
return (from info in informations
from period in info.Periods
where dateToCheck >= period.StartDate &&
dateToCheck < period.EndDate
select info).FirstOrDefault();
}
Upvotes: 0
Reputation: 21825
Problem with your query is TakeWhile, as per MSDN:-
Returns elements from a sequence as long as a specified condition is true. The element's index is used in the logic of the predicate function.
So in your query it is only checking untill it finds the correct match and after that it ignores all data. Instead you need to check your condition in entire periods
.
You can simplify your query using FirstOrDefault
and it should give you the correct result:-
return informations.FirstOrDefault(y => dateToCheck >= y.StartDate
& dateToCheck < y.EndDate);
This will return null if no match is found otherwise the first Information
object if it finds a match.
If you want to return all matching Periods
then use Where
instaed and your method return type should change to IEnumerable<Information>
instead of Information
.
Upvotes: 2