For completeness and flexibility, a couple of AppleScript and Javascript functions allowing for formats like M
, MM
, HH:MM
, and HH:MM:SS
minsFromHMS :: String -> Int
and
hoursFromMins :: Int -> Float
for example:
map(minsFromHMS, {"1:35:29", "1:35", "2:05", "76"})
--> {95, 95, 125, 76}
map(hoursFromMins, {95, 95, 125, 76})
--> {1.58, 1.58, 2.08, 1.27}
sum(map(hoursFromMins, {95, 95, 125, 76}))
--> 6.51
sum(map(hoursFromMins, map(minsFromHMS, ["1:35:29", "1:35", "2:05", "76"])))
--> 6.51
Applescript and Javascript source below
Applescript source
-- minsFromHMS :: String -> Int
on minsFromHMS(strHMS)
-- HOURS AND MINUTES AS SUM OF MINUTES
script sumOfParts
property digit : my isDigit
on |λ|(a, x, i, xs)
if all(digit, characters of x) then
if 0 ≠ (i - (length of xs)) then
set m to 60
else
set m to 1
end if
a + (m * (x as integer))
else
a
end if
end |λ|
end script
set xs to splitOn(":", strHMS)
set mins to foldl(sumOfParts, 0, take(2, xs))
-- PLUS ONE MINUTE IN THE CASE OF 30 OR MORE TRAILING SECONDS
set final to item -1 of xs
if 2 < length of xs and ¬
all(my isDigit, characters of final) and ¬
30 ≤ (final as integer) then
1 + mins
else
mins
end if
end minsFromHMS
-- hoursFromMins :: Int -> Float
on hoursFromMins(n)
(round (100 * (n / 60))) / 100
end hoursFromMins
-- TEST ------------------------------------------------------------------
on run
minsFromHMS("2:05") --> 125
hoursFromMins(125) --> 2.08
map(minsFromHMS, ["1:35:29", "1:35", "2:05", "76"]) --> {95, 95, 125, 76}
map(hoursFromMins, {95, 95, 125, 76}) --> {1.58, 1.58, 2.08, 1.27}
sum(map(hoursFromMins, map(minsFromHMS, ["1:35:29", "1:35", "2:05", "76"]))) --> 6.51
end run
-- GENERIC FUNCTIONS -----------------------------------------------
-- https://github.com/RobTrew/prelude-applescript
-- Applied to a predicate and a list, `all` determines if all elements
-- of the list satisfy the predicate.
-- all :: (a -> Bool) -> [a] -> Bool
on all(f, xs)
tell mReturn(f)
set lng to length of xs
repeat with i from 1 to lng
if not |λ|(item i of xs, i, xs) then return false
end repeat
true
end tell
end all
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with i from 1 to lng
set v to |λ|(v, item i of xs, i, xs)
end repeat
return v
end tell
end foldl
-- isDigit :: Char -> Bool
on isDigit(c)
set n to (id of c)
48 ≤ n and 57 ≥ n
end isDigit
-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map
-- min :: Ord a => a -> a -> a
on min(x, y)
if y < x then
y
else
x
end if
end min
-- splitOn :: String -> String -> [String]
on splitOn(needle, haystack)
set {dlm, my text item delimiters} to ¬
{my text item delimiters, needle}
set xs to text items of haystack
set my text item delimiters to dlm
return xs
end splitOn
-- sum :: [Num] -> Num
on sum(xs)
script add
on |λ|(a, b)
a + b
end |λ|
end script
foldl(add, 0, xs)
end sum
-- take :: Int -> [a] -> [a]
-- take :: Int -> String -> String
on take(n, xs)
if class of xs is string then
if 0 < n then
text 1 thru min(n, length of xs) of xs
else
""
end if
else
if 0 < n then
items 1 thru min(n, length of xs) of xs
else
{}
end if
end if
end take
-- Tuple (,) :: a -> b -> (a, b)
on Tuple(a, b)
{type:"Tuple", |1|:a, |2|:b, length:2}
end Tuple
JavaScript Source
(() => {
'use strict';
// minsFromHMS :: String -> Int
const minsFromHMS = strHHMM => {
const
parts = strHHMM.split(':'),
final = parts[parts.length - 1],
mins = parts.slice(0, 2)
.reduce(
(a, x, i, xs) => a + (
parseInt(x) * (
Boolean(i - xs.length + 1) ? (
60
) : 1
)
), 0
);
return (2 < parts.length) &&
!isNaN(final) && (30 <= parseInt(final, 10)) ? (
1 + mins
) : mins;
};
// hoursFromMins :: Int -> Float
const hoursFromMins = n =>
Math.round(100 * (n / 60)) / 100;
// TEST --------------------------------------------------
const main = () => {
minsFromHMS('2:05'); // -> 125
hoursFromMins(125); // -> 2.08
map(minsFromHMS, ['1:35:29', '1:35', '2:05', '76']);
// -> {95, 95, 125, 76}
map(hoursFromMins, [95, 95, 125, 76]);
// -> {1.58, 1.58, 2.08, 1.27}
return sum(map(
hoursFromMins,
map(minsFromHMS, ['1:35:29', '1:35', '2:05', '76'])
));
//-> 6.51
};
// GENERIC FUNCTIONS --------------------------------------
// https://github.com/RobTrew/prelude-jxa
// map :: (a -> b) -> [a] -> [b]
const map = (f, xs) => xs.map(f);
// sum :: [Num] -> Num
const sum = xs => xs.reduce((a, x) => a + x, 0);
// MAIN ---
return main();
})();