28 September 2022

Sequence numbers and split arrays

Sequence numbers

Task 1 this week states that we are given a list of strings in the format aa9999 ie first 2 characters can be anything in a-z followed by 4 digits from 0-9, and we have to write a script to replace the first two characters with '00', '01', '02' etc.

As with many of these tasks, formatting the input to match Mohammad's examples is part - and sometimes most - of the challenge.

The task itself is simply a case of iterating over the supplied list with $j as the index, and replacing each term with:

sprintf('%02d%s', $j, substr($list[$j], 2))

which is the index, left padded with zeroes to make 2 digits, followed by characters 2 (zero-based) to the end of the original string.

Split arrays

We are given a list of strings containing 0-9 and a-z, like this:

['1 2', 'p q r', 's 3', '4 5 t']

We are required to separate the digits and the letters to produce;

[[1,2], [3], [4,5]] and [['p','q','r'], ['s'], ['t']]

Interestingly the formatting of the output is rather different from the input.

After a couple of false starts I decided that the best strategy was to treat the input simply as a string of characters. The only characters that matter are the digits, letters and commas. I append digits to $first and (single-quoted) letters to $second, in both cases appending ', '. When a comma is encountered in the input I append '], [' to both $first and $second.

It's then just a case of combining '$first and $second' and eliminating trailing commas and empty occurrences of [].


22 September 2022

Unique arrays and differing dates

Unique arrays

We are given list of arrayrefs and asked to write a script to remove duplicates, ie to eliminate references to arrays which are identical to earlier ones.  The examples show the arrays containing positive integers; I have generalised the problem to assume that the array elements can be uniquely represented as strings. This allows any type of number or character string or null, but not perhaps references.

So I wrote a sub signature(@a) which creates a string by concatenating the array elements with a suitable separator. I used 0xFF as the separator, which will not occur in the representation of any array elements described above.

It's then just a matter of going through the supplied list, checking that $seen{$signature} is null and if so adding the current list item to the output list and setting $seen{$signature} = 1.

As usual, there is some ambiguity in the task description, and, for example, my algorithm will treat all of the following as being identical: [1, 2], [1.0, 2.0], [1e0, 2e0], ['1', '2'].

Years and days

We are given two dates, $date1 and $date2 in the format YYYY-MM-DD. and required to write a script to find the difference between the given dates in terms of years and days only.

Date and time calculations are always complicated by leap years, and if time of day is included, by daylight saving time and in the extreme by leap seconds. Thankfully we only have leap years to consider in this task.

From the examples given we may deduce that a 'year' means the period between a 'date' (month and day) in one year until the same date in the following year, which could be 365 or 366 days. So what we are looking for is the number of 'years' from $date1 until the last 'date' preceding $date2, and then the number of days between that 'date' and $date2.

It's easy enough to find the number of days between two dates. Convert noon on both of them to Unix epoch seconds, divide by 86400 (seconds in a day) and round to an integer (in case one is in winter and the other in summer). You could then do the messy business of finding out how many 29 Feb dates were between the two dates ... hmm, a bit messy.

But there is an easier way. Start at $date1. Make a note of its month and day - call that $anniversary. Step forward day by day until you get to $date2. If the new day's month and day match $anniversary, increment $years and set $days to zero; else just increment $days.

The only slightly messy bit is incrementing the date, but timelocal/localtime make that easy enough.  Note that the Posix version of timelocal can handle signed 64 bit second values, so that covers any sensible dates.

If you're looking at really long gaps between $date1 and $date2 of course this method is relatively slow. But on my machine it comes up with Queen Elizabeth's lifespan as 96 years 140 days in just a few seconds.




15 September 2022

Find the biggest and the deepest

Where's the biggest item in a list?

We are given a list of integers and are to find the index of the first occurrence of the largest number in the list.

We are going to have to look at every number in the list. It therefore seems best to make a single pass through the list, keeping track of the largest value found so far, and its index. And that's the solution I have submitted.

I thought about solutions involving sorting the list, but that's overkill for such a simple requirement.

What's the deepest path leading to all the members of a list?

We are given a list of absolute Linux file paths and asked to determine the deepest path to the directory that contains all of them.

Let's start with the first item in the list - say /a/b/c/d/xx. We can immediately see that the longest possible answer is /a/b/c/d/, because any longer one would not be common to the first item.

So let's go down the list looking to see if they all start with /a/b/c/d/. As soon as we find one that doesn't, lets start again, stripping the deepest directory from our initial guess, which in our case leaves us with /a/b/c/.

Eventually we'll find a path that is common to all the items in the list, though that may simply be /.

So this week's tasks are relatively easy and solved in a few lines of code, but both bear thinking about.



05 September 2022

Alphabetical and days hotter sentences

Alphabetical sentences

Why would you ever want to do this? Only Mohammad knows. The obvious logic, and the way I did it was:

  • Split the text into sentences
  • Split each sentence into an array of words
  • Sort the array
  • Print it out
In order to match Mo's sample output I limited the length of each output line to 60 chars, which adds a couple of lines to the script.

Hotter days

We've had a few hot days recently so this is topical and perhaps a little more practical than the sentences.

The obvious way to do it is to read the the data into an array such that $input{date} = temp, and then with 'for $date (sort keys @input) ' we can print the desired output in a single line of code.