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.




No comments:

Post a Comment