14
\$\begingroup\$

Warning: DO NOT take medical advice from this post. If you want medical advice, go to a qualified professional.

I have a headache. I need headache pills.

I'll tell you the last few doses I've had, and you tell me when I can have my next dose, without overdosing.

I'll give you this string: P: 00:00, I: 02:00, P: 04:00, I: 06:00

And you'll give me this: Next P: 08:00, I: 10:00

Input:

String representing the times each medication has been taken, in the following format:

P: 00:00, I: 02:00, P: 04:00, I: 06:00

This means Paracetamol was taken at 00:00 and 04:00, and Ibuprofen was taken at 02:00 and 06:00

Output (updated):

String with the time the next does of each medication can be taken, in the following format:

Next P: 08:00, I: 10:00
  • The output order should be in the order which the medication is to be taken. - If Ibuprofen is to be taken at 09:35 and Paracetamol and 10:22, then the output should be Next I: 09:35, P: 10:22
  • If the times for the next dose of each medication are the same, the output order doesn't matter: Next P: 08:00, I: 08:00 OR Next I: 08:00, P: 08:00
  • If only one medication is being taken (in the input string), then only that medication should be in the output string: Next P: 02:00

Rules:

  • There will only ever be two types of medication, Paracetamol 'P' and Ibuprofen 'I'.
  • Paracetamol can be taken once every 4 hours, a maximum of 4 times within a 24-hour period.
  • Ibuprofen can also be taken once every 4 hours, a maximum of 4 times within a 24-hour period.
  • Paracetamol and Ibuprofen can be taken together, or at separate times. One doesn't count towards the dosage of the other.
  • The times in the input string will always be consecutive, but may roll over midnight (23:00 -> 03:00)
  • The times in the input string will not span more than 24 hours
  • Maximum of 4 times for each medication (8 max in total)
  • Input will always be non-empty and contain at least one medication and one time

Examples:

Two doses of each at two hour intervals:

"P: 00:00, I: 02:00, P: 04:00, I: 06:00" -> "Next P: 08:00, I: 10:00"

Single dose of Paracetamol

"P: 22:00" -> "Next P: 02:00"

Maximum Paracetamol dose within 24 hours, single Ibuprofen dose

"P: 04:05, P: 08:10, P: 12:15, I: 12:30, P: 16:25" -> "Next I: 16:30, P: 04:05"

Test cases:

"I: 06:00" -> "Next I: 10:00"
"P: 22:00" -> "Next P: 02:00"
"P: 22:00, P: 02:00, I: 06:00" -> "Next P: 06:00, I: 10:00"
"P: 00:00, I: 02:00, P: 04:00, I: 06:00" -> "Next P: 08:00, I: 10:00"
"P: 04:05, P: 08:10, P: 12:15, I: 12:30, P: 16:25" -> "Next I: 16:30, P: 04:05"
"I: 06:32, P: 08:15, I: 10:44, P: 13:03" -> "Next I: 14:44, P: 17:03"
"P: 07:30, I: 07:30, P: 11:30, I: 11:30, P: 15:30, I: 15:30, I: 19:30" -> "Next P: 19:30, I: 07:30"
"I: 07:30, P: 11:30, I: 11:30, P: 15:30, I: 15:30, P: 19:30, I: 19:30" -> "Next P: 23:30, I: 07:30"
"P: 07:30, I: 07:30, P: 11:30, I: 11:30, P: 15:30, I: 15:30, P: 19:30, I: 19:30" -> "Next P: 07:30, I: 07:30" OR "Next I: 07:30, P: 07:30"

This is code golf, so the shortest answer int bytes wins.

UPDATE:

The output can now be abbreviations of Paracetamol and Ibuprofen; P and I

\$\endgroup\$
  • \$\begingroup\$ I'd be nice to have some leverage on the input and output format - meta post \$\endgroup\$ – Gurupad Mamadapur Dec 25 '16 at 14:22
  • \$\begingroup\$ @GurupadMamadapur output maybe, but extracting the times and medication type from the input is half the challenge \$\endgroup\$ – Erresen Dec 25 '16 at 14:26
  • \$\begingroup\$ I'd recommend you allow people to abbreviate paracetamol and ibuprofen in output as they add unnecessary length to submissions \$\endgroup\$ – Cyoce Dec 26 '16 at 21:12
  • \$\begingroup\$ @Cyoce yeah I agree, trying a solution myself and it's actually a bit tricky - updated the rules to allow abbreviated output \$\endgroup\$ – Erresen Dec 26 '16 at 21:24
  • \$\begingroup\$ @Lynn agreed & updated \$\endgroup\$ – Erresen Dec 26 '16 at 22:50
4
\$\begingroup\$

JavaScript (ES6), 367 362 354 358 bytes

Golfed version:

A=i=>i>9?""+i:"0"+i,B=(s,a=":")=>s.split(a),C=(a,b,c,d)=>[...[s,t]=B((b>3?c:d)||":"),a+` ${A(s=b>3?+s:(+s+4)%24)}:`+A(t=+t)],F=s=>{a=B(s,m=", ");for(b=c=d=e=f=p=q=0;f<a.length;g=="P:"?(b++,d=d?h:p=h):(c++,e=e?h:q=h))[g,h]=B(a[f++]," ");[i,j,k]=C("P",b,p,d),[n,o,l]=C("I",c,q,e),r=B(h)[0];return"Next "+c?b?n*60+(n<r)*1440+j<i*60+(i<r)*1440+o?l+m+k:k+m+l:l:k}

Ungolfed/commented:

// Returns a zero-padded string of the argument.
A=i=>i>9?""+i:"0"+i,

// Since we do a lot of splitting, alias it. Making the
// second argument optional (and defaulting to ':') saved
// 3 bytes
B=(s,a=":")=>s.split(a),

// Constructs a string for output, along with the time
// of the next dose, in the format [hour, minute, string].
// Arguments:               type
// a -> type (P/I)          String
// b -> amount of doses     Number
//      taken
// c -> first dose taken    String
// d -> last dose taken     String
//
// The first two values are split from the string, but
// before the array is returned, they are converted to
// integers (during the string construction).
C=(a,b,c,d)=>[...[s,t]=B((b>3?c:d)||":"),a+` ${A(s=b>3?+s:(+s+4)%24)}:`+A(t=+t)],

// Main function. Returns the time(s) for the next dose.
// Argument:                type
// s -> list of times of    String
//      and types of 
//      doses taken
F=s=>{
    a=B(s,m=", "); // Split the input by comma + space,
                   // and save that string, since we
                   // need it later when constructing
                   // the output string.
    // For loop has been restructured. Original:
    // for(b=c=f=0;f<a.length;g=="P:"?(b++,d=d?h:p=h):(c++,e=e?h:q=h))
    //     [g,h]=B(a[f++]," ");
    b = 0; // P-dose counter
    c = 0; // I-dose counter
    d = 0; // Last P-dose
    e = 0; // Last I-dose
    p = 0; // First P-dose
    q = 0; // First I-dose
    for (f = 0; f < a.length; f++) {
        [g, h] = B(a[f], " ");  // g contains the type,
                                // h contains the time
        if (g == "P:") {
            b++;                // increase the counter

            if (d == 0) {   // store h in p if this is
                p = h;      // the first dose of this
            }               // type
            d = h;
        } else {
            // See the above code block for comments
            c++;

            if (e == 0) {
                q = h;
            }
            e = h;
        }
    }
    // End of restructured for loop.

    // Construct the output strings, and get the times.
    // See comments at C function.
    [i, j, k] = C("P", b, p, d);
    [n, o, l] = C("I", c, q, e);

    // Get the amount of hours of the dose taken last.
    // We use this to get the correct order of the two
    // times.
    r = B(h)[0];

    // Return statement has been restructured. Original:
    // return "Next "+c?b?n*60+(n<r)*1440+j<i*60+(i<r)*1440+o?l+m+k:k+m+l:l:k
    //==================================================
    // Start creating the output string.
    output = "Next "
    // Use the following checks to figure out what needs
    // to be part of the output and in what order.
    if (c > 0) {
        if (b > 0) {
            // Compare the times of next doses
            // P_time = (i + (i < r) * 24) * 60
            // I'm using implicit conversion of
            // booleans to numbers. If the next
            // dose is past midnight, add 1 * 24
            // to the time, so it is compared
            // correctly.
            // Then add the minutes to the number.
            P_time = i*60+(i<r)*1440+o;
            I_time = n*60+(n<r)*1440+j;

            if (I_time < P_time) {
                output += l + m + k; // I first
            } else {
                output += k + m + l; // P first
            }
        } else {
            output += l; // Just I
        }
    } else {
        output += k; // Just P
    }

    // Finally, return the output
    return output;
}

To use it, call F with the string as argument like so:

F("P: 04:00, I: 06:00")
\$\endgroup\$
  • \$\begingroup\$ This is great, but I had a couple of problems. It seems to fail if there is only one type of pill in the input, e.g. F("P: 22:00") -> ReferenceError: q is not defined. This input will run if P&I have been referenced previously, but with old details for I. \$\endgroup\$ – Chris M Dec 30 '16 at 21:46
  • \$\begingroup\$ Thanks! I just tested it and you're right about the reference error. I guess the q variable isn't reset and I didn't pay enough attention during testing. Thanks for letting me know, I'll fix it later. \$\endgroup\$ – Luke Dec 30 '16 at 23:19
  • \$\begingroup\$ Turned out to be an easy fix, but it cost me 4 bytes. \$\endgroup\$ – Luke Dec 30 '16 at 23:29
1
\$\begingroup\$

Python 3 - 437 bytes

a=input();i=p=l=-1;j=q=0
for x in a.split(", ")[::-1]:
    for y, z in [x.split(": ")]:
        s=lambda q,r,t:[t,sum([a*b for a,b in zip([60,1],map(int,q.split(':')))])][r%4<2]+[0,240][r<2]
        if y=="I":j+=1;i=s(z,j,i)
        else:q+=1;p=s(z,q,p)
        l=[l,p+i-239][j+q<2]
r=lambda d,e:("","%s: %02d:%02d, "%(d,(e/60)%24,e%60))[e>-1];p+=[1440,0][p>=l];i+=[1440,0][i>=l];print("Next "+[r("I",i)+r("P",p),r("P",p)+r("I",i)][p<i][:-2])

Explanation:

a=input();i=p=l=-1;j=q=0
for x in a.split(", ")[::-1]: #Read in reverse order, a="P: 01:00"
    for y, z in [x.split(": ")]:#Y="P", Z="00:00"
        s=
        lambda q,r,t:[t,sum([a*b for a,b in zip([60,1],map(int,q.split(':')))])]#Convert "01:01" to 61
        [r%4<2]#In case it's the first or fourth string calculate a new value, otherwise: return the original value
        +[0,240][r<2]#In case it's the last string: add 4 hours. Otherwise, leave it.
        if y=="I":j+=1;i=s(z,j,i)#Calculate for i
        else:q+=1;p=s(z,q,p)#Calculate for p
        l=[l,p+i-239][j+q<2]#Sets the last record. Since we read in reverse order, this should be the first one. We've added 4 hours though so remove those again
r=lambda d,e:("","%s: %02d:%02d, "%(d,(e/60)%24,e%60))[e>-1];#Print function, don't print anything when we have no value
p+=[1440,0][p>=l];i+=[1440,0][i>=l];    #Add a day if record is before the last record so we can correctly calculate the order
print("Next "+[r("I",i)+r("P",p),r("P",p)+r("I",i)][p<i][:-2])#print it and remove the last ","
\$\endgroup\$
1
\$\begingroup\$

PHP, 228 241 239 227 226 bytes

requires PHP 7

Next<?foreach(explode(", ",$argv[1])as$d){[$m,$h,$i]=explode(":",$d);$x[$m][++$$m]=24+$h+$i/60;}foreach($x as$m=>$d)$r[$m]=$d[$$m-3]?:$d[$$m]-20;sort($r);foreach($r as$m=>$t)$o[]=" $m: ".date("i:s",$t%24*60);echo join(",",$o);

breakdown

Next<?                              // print "Next"
foreach(explode(", ",$argv[1])as$d) // loop through string split by comma+space
{
    [$m,$h,$i]=explode(":",$d);         // separate drug, hours and minutes
    $x[$m][++$$m]=24+$h+$i/60;          // append time to array, track count in ${$m}
}                                       // (i.e. $P for drug "P" etc.)
foreach($x as$m=>$d)                // loop through drugs
    $r[$m]=                             // add time to result
        $d[$$m-3]                           // if more than 3 medications, use $$m-3
            ??$d[$$m]-20                    // else use last medication - 20 hours
    ;
sort($r);                           // sort results by time
foreach($r as$m=>$t)$o[]=" $m: "    // prepare for output: drug name and formatted time:
    .date("i:s",$t%24*60)           // use hrs as mins and mins as secs to avoid TZ problems
;
echo join(",",$o);                  // print
\$\endgroup\$
0
\$\begingroup\$

JavaScript (ES6), 246 bytes

s=>s.split`, `.map(s=>(m[s[0]].unshift(t=s.replace(/\d+/,h=>(h=(1+h)%24)>9?h:`0`+h),s),l=l||t.slice(1)),l=0,m={I:[],P:[]})&&`Next `+[].concat(m.I[7]||m.I[0]||[],m.P[7]||m.P[0]||[]).sort((i,p)=>((i=i.slice(1))<l)-((p=p.slice(1))<l)||i>p).join`, `

Explanation:

Looping over each dose, the I and P doses are separated into two arrays. 4 hours is also added to each dose, and those times are saved too. The arrays are populated in reverse to make detecting 8 entries easier. The time 4 hours after the first dose is also saved for use during sorting. At this point each array can be in one of three states:

  • 8 entries, in which case the last entry is the first dose, and the next dose must be 24 hours after this dose (i.e. the same time tomorrow)
  • 2, 4 or 6 entries, in which case the first entry is 4 hours after the last dose, and therefore the time of the next dose
  • 0 entries, in which case we concatentate [], which gets flattened and therefore excluded from the result

Having extracted the next dose times from the two arrays, it remains to sort them into order. This is done by comparing them against the time 4 hours after the first dose. If one of the two times is before this time, this must refer to tomorrow, and that dose comes last. Otherwise, the times are simply compared directly. (Rather inconveniently, the medicine is before the time, so I have to strip it to compare properly.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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