Earlier this year a friend sent me a video showing how he implemented a phone bill calculation challenge using Scala. I took a stab at it using Java + Data Pipeline and below is what I came up with.
How about you? How would you code this using your favourite language or framework?
The Scala Approach
You can watch Ervin’s walk-through of his Scala solution:
http://www.hardcomsoft.com/videos/scala_sample_billCalculation.html
You can also read the original requirements for yourself.
The Data Pipeline Approach
Here’s the Data Pipeline code I put together.
- It uses a CSVReader to load the data from a string.
- It then uses a series of TransformingReaders to convert the duration to seconds and conditionally calculate the price depending on whether the duration is over 5 minutes or not.
- Next it uses a GroupByReader to sum the price and duration and to count the calls before sorting them and dropping the one with the highest duration.
- Finally, another GroupByReader is used to sum all the prices together to find and return the grand total.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
import java.io.StringReader; import com.northconcepts.datapipeline.core.DataReader; import com.northconcepts.datapipeline.core.LimitReader; import com.northconcepts.datapipeline.core.SortingReader; import com.northconcepts.datapipeline.csv.CSVReader; import com.northconcepts.datapipeline.filter.FilterExpression; import com.northconcepts.datapipeline.group.GroupByReader; import com.northconcepts.datapipeline.job.Job; import com.northconcepts.datapipeline.memory.MemoryWriter; import com.northconcepts.datapipeline.transform.RenameField; import com.northconcepts.datapipeline.transform.SetCalculatedField; import com.northconcepts.datapipeline.transform.SetField; import com.northconcepts.datapipeline.transform.TransformingReader; public class ScratchPhoneCharges { private static String INPUT = "00:01:07,400-234-090\n" + "00:05:01,701-080-080\n" + "00:05:00,400-234-090"; private static final int SECOND_FEE = 3; private static final int MINUTE_FEE = 150; private static final int FEE_CHANGE_THRESHOLD_IN_MIN = 5; public static void main(String[] args) { int priceInCents = solution(INPUT); System.out.println("price in cents = " + priceInCents); } public static int solution(String s) { DataReader reader = new CSVReader(new StringReader(s)); reader = new TransformingReader(reader) .add(new RenameField("A", "duration")) .add(new RenameField("B", "phone")) .add(new SetCalculatedField("duration_seconds", "(toInt(substring(duration, 0, 2)) * 3600) + " + "(toInt(substring(duration, 3, 5)) * 60) + " + "toInt(substring(duration, 6, 8))")); // if less than 5 minutes reader = new TransformingReader(reader) .setCondition(new FilterExpression("duration_seconds < " + FEE_CHANGE_THRESHOLD_IN_MIN + " * 60")) .add(new SetField("rate_units", "per second")) .add(new SetField("rate", SECOND_FEE)) .add(new SetCalculatedField("price", "duration_seconds * rate")); // if 5 minutes or more reader = new TransformingReader(reader) .setCondition(new FilterExpression("duration_seconds >= " + FEE_CHANGE_THRESHOLD_IN_MIN + " * 60")) .add(new SetField("rate_units", "per minute")) .add(new SetField("rate", MINUTE_FEE)) .add(new SetCalculatedField("price", "(duration_seconds+59)/60 * rate")); reader = new GroupByReader(reader, "phone").sum("price").sum("duration_seconds").count("calls"); reader = new SortingReader(reader).desc("duration_seconds").asc("phone"); // skip first record (phone with highest duration) reader = new LimitReader(reader).setOffset(1); reader = new GroupByReader(reader).sum("price").sum("calls"); MemoryWriter writer = new MemoryWriter(); Job.run(reader, writer); return writer.getRecordList().get(0).getField("price").getValueAsInteger(); } } |
What’s your Approach?
I’d love to see other approaches in any programming language or framework, but other Scala and Java approaches would be great to see.
Happy Coding!
Great post! I wrote up a response solution in vanilla Java if you’d like to take a look!
Thanks for the pure Java approach Devin. Glad you had fun 🙂