Serenity
Serenity

Reputation: 33

reading JSON data and loop through the data to get the certain value based on conditions

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:

enter image description here

The expected output will be like:

Thanks

Upvotes: 3

Views: 752

Answers (3)

Christos
Christos

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

Arnaud F.
Arnaud F.

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

Rahul Singh
Rahul Singh

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

Related Questions