Specification:
Given a string (e.g., the content of an article), print the estimated reading time by bucketizing time into increasingly large chunks in the form "5-10 min" or "48 min-2 hr".
Notes:
Distribution of time into buckets is arbitrary, to make it "look nice" for the default 5-minute time chunks. This is okay.
60 minutes exactly is special-cased, so that it should be "30-60 min" instead of "30 min-1 hr".
The "real" application of this code is inside a plugin for something else; rather than printing verbose information like in the code below, it actually returns
$time_label
.I don't normally work in PHP, so any "idiomatic" feedback is also welcome.
Sample Input:
print_reading_time("This is a test.");
print_reading_time(str_repeat("test ", 100));
print_reading_time(str_repeat("test ", 1000));
print_reading_time(str_repeat("test ", 10000));
print_reading_time(str_repeat("test ", 20000));
print_reading_time(str_repeat("test ", 10000), 8);
Sample Output:
estimated reading time: 0 min (0-5 min), based on 4 words at 180 WPM
estimated reading time: 1 min (0-5 min), based on 100 words at 180 WPM
estimated reading time: 6 min (5-10 min), based on 1000 words at 180 WPM
estimated reading time: 56 min (30-60 min), based on 10000 words at 180 WPM
estimated reading time: 111 min (2 hr), based on 20000 words at 180 WPMestimated reading time: 56 min (48 min-2 hr), based on 10000 words at 180 WPM
Original:
<?php
function print_reading_time($content, $minimum_time_bucket = 5 /* minutes */) {
$wpm = 180; // arbitrary value, based on adult averages
$word_count = str_word_count($content);
$minutes = round($word_count / $wpm);
$time_label = minutes_to_time_label($minutes, $minimum_time_bucket);
print("estimated reading time: $minutes min ($time_label), " .
"based on $word_count words at $wpm WPM");
}
function minutes_to_time_label($minutes, $minimum_time_bucket) {
// Buckets: <5, 5-10, 10-15, 15-30, 30-60, then 1-hour increments,
// for the default 5-minute minimum bucket size.
$buckets = array(0, 1, 2, 3, 6, 12);
for ($i = 0; $i < count($buckets) - 1; $i++) {
$lowerBound = $buckets[$i] * $minimum_time_bucket;
$upperBound = $buckets[$i+1] * $minimum_time_bucket;
if ($lowerBound <= $minutes && $minutes < $upperBound) {
$lowerOptions = array();
// If the amounts are both in minutes or both in hours, don't display
// the units for the first amount (ie, show "5-10 min", not "5 min-10 min".
if (($lowerBound <= 60 && $upperBound <= 60) || ($lowerBound > 60 && $upperBound > 60)) {
$lowerOptions["no_units"] = true;
}
return minutes_to_time_str($lowerBound, $lowerOptions) . "-"
. minutes_to_time_str($upperBound);
}
}
// If we get to here, then the time is longer than our largest explicit bucket.
// Divide everything beyond into hour-long buckets.
return minutes_to_time_str($minutes, array("treat_60_min_as_an_hour" => true));
}
function minutes_to_time_str($minutes, $options = array()) {
$options = array_merge(default_options(), $options);
$amount = $minutes;
$units = "min";
if ($minutes > 60 || ($minutes === 60 && $options["treat_60_min_as_an_hour"])) {
$amount = round($minutes / 60);
$units = "hr";
}
return $amount . ($options["no_units"] ? "" : " $units");
}
function default_options() {
return array(
"no_units" => false,
"treat_60_min_as_an_hour" => false,
);
}
?>