# How to organize a list of dates

I have a list of daily measurements taken on given dates, where the measurements follow the dates:

lis = {"a", "b", "October", "01", "2021", "c", "d", "October", "1", "2021","e", "October", "2", "2021", "f"}


I would like to organize these measurements as discrete list members as follows:

res = {{DateObject[{2021,10,1}],"c","d"},{DateObject[{2021,10,1}],"e"},{DateObject[{2021,10,2}],"f"}}


Thanks for any ideas!

• Sounds like an awful format. Do you know that there will always be two entries at the front to discard? Can you get the data in a better format to start with? Oct 5 at 17:29
• It is a horrible format, I totally agree (that's why I'm here :) There can be any number of entries to discard at the front. Oct 5 at 17:42
• Based on the subject, maybe take one to a movie, another bowling,...? Oct 5 at 19:55

Mathematica solutions fit into many categories and take their readers through different abstract states (pause, admiration, disbelief, bewilderment etc). I present a step by step solution mostly for new learners. Given:

lis = {"a", "b", "October", "01", "2021", "c", "d", "October", "1",
"2021", "e", "October", "2", "2021", "f"}


Define months; Remember that month strings could be shortened as "Jan", "Feb" etc or these could be lower case or in another language, but let's work with standard English strings for now:

months = {"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"}


{"January" -> 1, "February" -> 2, "March" -> 3, "April" -> 4,
"May" -> 5, "June" -> 6, "July" -> 7, "August" -> 8,
"September" -> 9, "October" -> 10, "November" -> 11,
"December" -> 12}


Define a monthQ function to see if an input string is a month. Test the function:

monthQ[s_String] := MemberQ[months, s]
monthQ[#] & /@ {"Banana", "October", "April", "Carrot"}

{False, True, True, False}


Main solution starts here-----------

Since "October" is the marker where things need to be split, find positions where it (or another month name string) occurs.

pos = Position[MemberQ[months, #] & /@ lis, True]


{{3}, {8}, {12}}

Now insert a marker at these positions where split will happen, (* U2702 = ✂ *)

lis2 = Insert[lis, "✂", pos]

{"a", "b", "✂", "October", "01", "2021", "c", "d", "✂", "October", \
"1", "2021", "e", "✂", "October", "2", "2021", "f"}

lis2a = SequenceSplit[lis2, {"✂"}]

{{"a", "b"}, {"October", "01", "2021", "c", "d"}, {"October", "1",
"2021", "e"}, {"October", "2", "2021", "f"}}


The first list could be without a month marker. I will pretend that there could be others as a result of some other processing. Select only those lists that have a month-string as their first element.

lis3 = Select[lis2a, monthQ[First[#]] &]

{{"October", "01", "2021", "c", "d"}, {"October", "1", "2021",
"e"}, {"October", "2", "2021", "f"}}


Now write a utility function to parse the first three members of each list as a date object. Be as simple and modular as possible, as such juxtapositions can change.

dobjparse[k_List] := Module[{t1, t2, t3},
t1 = ToExpression[(k[[1]] /. morules)];
(*Echo[t1];*)
t2 = ToExpression[k[[2]]];
(*Echo[t2];*)
t3 = ToExpression[k[[3]]];
(*Echo[t3];*)
DateObject[{t3, t1, t2}]
]


Now apply this utility function to appropriate sections of lis3 and keep the rest of the sublist elements:

{dobjparse[#[[1 ;; 3]]], Sequence @@ #[[4 ;; -1]]} & /@ lis3


This solution has added nothing new compared to Bob Hanlon's elegant and concise solution; however, the advantage of this solution is that it is modular and can be modified by users at various Mma skill levels should they need to.

Clear["Global*"]

\$Version

(* "12.3.1 for Mac OS X x86 (64-bit) (June 19, 2021)" *)

months = {"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};

monthQ[mo_String] := MemberQ[months, mo]

lis = {"a", "b", "October", "01", "2021", "c", "d", "October", "1",
"2021", "e", "October", "2", "2021", "f"};


Use SequenceCases to select the groupings. Then format the groupings.

lis2 = {DateObject[#[[1 ;; 3]] /. monthRule // ToExpression //
RotateRight], Sequence @@ #[[4 ;;]]} & /@
SequenceCases[lis, { _?monthQ, _?(! monthQ[#] &) ..}]


• Thanks @Bob Hanlon and @kglr! For these solutions to work, I need to first clean up the list by somehow deleting all list elements that appear before the first occurrence of the pattern: {,"Saturday","October","02","2021",} in the list. Again thx for ideas. Oct 5 at 21:10
• @Suite401 - If you mean you want to delete all items prior to Oct 2, 2021 use Select[lis2, DateDifference[DateObject[{2021, 10, 2}], #[[1]]] > Quantity[-1, "day"] &] Oct 5 at 22:10
• @Suite401, In the solution I have posted, it is quite easy to filter the first sublist if it doesn't contain the desired pattern.
– Syed
Oct 6 at 9:50
• Thank you @Syed Oct 6 at 15:29
ClearAll[reOrg]
reOrg = {DateObject @ DateString @ StringRiffle[{#, #2, #3}, "/"], ##4} &;

ipdQ = InternalPossibleDateQ;

SequenceCases[lis,
p : {_?ipdQ, _, _?ipdQ, Except[_?ipdQ] ..} :> reOrg @@ p]


Alternatively,

reOrg @@@ SequenceCases[lis, {_?ipdQ, _, _?ipdQ, Except[_?ipdQ] ..}]