1
$\begingroup$

I have a function timeToMinutes that takes a 24-hour time as a string argument (in digital clock format, e.g., "15:47") and converts it to the number of minutes past the last midnight. For instance, timeToMinutes["3:45"] yields 225.

Here is the function as I have written it:

timeToMinutes[time_] := 
  Function[pair, 60*pair[[1]] + pair[[2]]]@(ToExpression /@ time~StringSplit~":");

First it splits the string at the colon, then converts the two resultant strings to a pair of expressions, then sums 60 times the first member of the pair and one times the second member of the pair. This works fine, but I would like to rewrite it using pattern matching. What I have in mind is something like:

newTimeToMinutes = Function[time:(ToString[hours_]~~":"~~ToString[mins_]), 60*hours + mins]

The argument time would be dissected into the two numbers on either side of the colon, furthermore, the numbers on either side of the colon would be used in their numerical form instead of their string form (this is why I would like it if I could use ToString[hours_] to recognize something like "45" as the number 45.).

Is this possible? It seems like my problem is that patterns can be used in the arguments for arbitrary functions so long as they're patterns on expression input. On the other hand, the built-in string pattern tools that can pattern match on strings like StringReplace expect to take a string in and return a string out.

$\endgroup$
1

2 Answers 2

4
$\begingroup$

String is an atomic object, which means that you can't deconstruct it like you propose, just like you can't expect f[a_b_c_] to match f[123]. You will have to use functions that specifically operate on string objects to deconstruct them.

You can rewrite your function using string patterns as:

f[time_] := StringCases[time, hrs__ ~~ ":" ~~ mins__ :> {60, 1}.ToExpression@{hrs, mins}]
$\endgroup$
1
  • $\begingroup$ You beat me this time. :-) $\endgroup$
    – Mr.Wizard
    Mar 16, 2013 at 5:15
5
$\begingroup$

Here is an alternative definition that's a little shorter:

newTime[t_] := FromDigits /@ StringSplit[t, ":"].{60, 1}

newTime["14:30"]

(* ==> 870 *)

And to come back to the question of patterns in the function argument: you can certainly use patterns to restrict the type of arguments to which this definition applies. Here I'll combine two things: a test for String arguments, and an additional condition to make sure there's a colon in the string in the first place:

Clear[newTime]

newTime[t_String] /; 
  StringMatchQ[t, __ ~~ ":" ~~ __] := (FromDigits /@ StringSplit[t, ":"]).{60, 1}

newTime["12:22"]

(* ==> 742 *)
$\endgroup$
4
  • 1
    $\begingroup$ I recommend t_String over t_?StringQ as it's faster and simpler. (+1) $\endgroup$
    – Mr.Wizard
    Mar 16, 2013 at 6:44
  • $\begingroup$ Your use of condition is not what the OP intended though. The OP wanted to do something like the equivalent of f[{a_,b_}] := ... for f[{1,2}] for strings i.e., deconstruct it in place $\endgroup$
    – rm -rf
    Mar 16, 2013 at 14:49
  • $\begingroup$ @Mr.Wizard Right, I changed to _String. $\endgroup$
    – Jens
    Mar 16, 2013 at 15:24
  • $\begingroup$ @rm-rf Sure, and as you said it can't be done. But I implemented what I think the OP originally had in mind. $\endgroup$
    – Jens
    Mar 16, 2013 at 15:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.