# converting a raw string into a Mathematica list

I have a set of data consisting of daily measurements of two parameters. The data is available as a list of strings. Different parameters are measured each day. In the sample below, on January 1, a parameter "ab" has the value 100 and "c" has the value 101. (My data has undesirably split the names of the parameters wherever the name contains more than one word.)

lis = {"January","1","a","b","100","c","101","January","2","c","de","f","102","g","h",103"}


and would like to convert this to:

res ={{DateObject[{2020, 1, 1}],"ab","100","c","101"},{DateObject[{2020,1,2}],"cdef","102","gh","103"}}


In this data, there is always a day number following a month name. The parameter names are always preceded and followed by numbers and need to be combined into a single string element. Thank you for any suggestions.

• Are you really really sure that you can no longer fix the original and prevent the splitting? This is a pretty horrible format, and a lot of work. Also, how come "de" did not get split? Jan 10, 2020 at 22:59
• Also, what happened to e.g. January 10? Did it become {"January", "1", "0"}, or {"January", "10"}? Jan 10, 2020 at 23:08

Gosh, what a mess! Pretty much the only thing that could be easily recognized is the month names, so let's start from there to tokenize the list of strings.

I have added another example to your list, just to make sure that it works for more than two examples, and defined a list of months:

list = {"January", "1", "a", "b", "100", "c", "101",
"January", "2", "c", "de", "f", "102", "g", "h", "103",
"February", "20", "a", "b", "20"};

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


Start by finding the month names and using them as delimiters to split the list:

Flatten@Position[list, _String?(MemberQ[months, #] &)]~
Join~{Length[list] + 1};
Range[#1, #2 - 1] & @@@ Partition[%, 2, 1];
chopped = list[[#]] & /@ %

(* Out:
{{"January", "1", "a", "b", "100", "c", "101"},
{"January", "2", "c", "de", "f", "102", "g", "h", "103"},
{"February", "20", "a", "b", "20"}} *)


The use an Interpreter to turn the first two strings, guaranteed to be month and day, to a DateObject:

dated = Cases[
chopped,
{month_, day_, rest__} :> {Interpreter["Date"][StringJoin[month, day]], rest}
]

(* Out:
{{DateObject[{2020, 1, 1}, "Day", "Gregorian", -6.], "a", "b", "100", "c", "101"},
{DateObject[{2020, 1, 2}, "Day", "Gregorian", -6.], "c", "de", "f", "102", "g", "h", "103"},
{DateObject[{2020, 2, 20}, "Day", "Gregorian", -6.], "a", "b", "20"}}
*)


Finally define a conversion function that uses ToExpression to convert each token, then decide whether the converted form is a string (i.e. a measurement name), or a number (i.e. a measurement value); in the latter case, it returns the number, otherwise it returns the string.

ClearAll[converter]
converter[s_String] :=
StringCases[s,
PatternSequence[
name : Repeated[LetterCharacter],
value : Repeated[DigitCharacter]
] :> Sequence[name, ToExpression[value]]
]


... and apply it to the previous result:

Cases[
dated,
{date_DateObject, strings__} :> {date, Sequence @@ converter@StringJoin[strings]}
]

(* Out:
{{DateObject[{2020, 1, 1}, "Day", "Gregorian", -6.], "ab", 100, "c", 101},
{DateObject[{2020, 1, 2}, "Day", "Gregorian", -6.], "cdef", 102, "gh", 103},
{DateObject[{2020, 2, 20}, "Day", "Gregorian", -6.], "ab", 20}}
*)

• Yes it is a mess. But thanks for a start at it. Jan 11, 2020 at 4:21
• @Suite401 You are welcome. See my update answer: it should solve the whole problem. Jan 12, 2020 at 2:11
• Thanks! a lot to study here. Jan 12, 2020 at 6:24