Peter’s blog ✴ Week 283 ✴ 19 August 2024

THE WEEKLY CHALLENGE
Occurrences

The Perl Camel

Task 1

Unique number

You are given an array of integers, @ints, where every element appears more than once except one element. Write a script to find the one element that appears exactly one time.

Examples


Example 1
Input: @ints = (3, 3, 1)
Output: 1

Example 2
Input: @ints = (3, 2, 4, 2, 4)
Output: 3

Example 3
Input: @ints = (1)
Output: 1

Example 4
Input: @ints = (4, 3, 1, 1, 1, 4)
Output: 3

Analysis

My first idea for solving this was to concatenate the members of @ints into $string, enclosing each element with some sort of brackets. I tried it with <> as those characters are not special in a regular expression. So:

1, 2, 2, 3, 3 => <1><2><2><3><3>

I can then do this:

for $i (@ints) {
	if (@string !~ m|<$i>.*<$i>|) {
		... $i is the answer

That works for the given examples, and indeed for any example I could think of. But it is slightly inefficient in that it will check the duplicated numbers more than once if they appear more than once before the unique one.

So I went for a 'purer' solution, which is simply to count the frequency $freq{$i} of all the numbers $i, and then to loop through %freq to find the first $freq{$i} having a value of 1.

Neither of these solutions tests the validity of the assertion that there is one and only one element of @ints that occurs just once, and in a production environment it would of course be very advisable to do that.

Perl Weekly’s review

from Perl Weekly issue 683

Try DIY tool to play with your sample data. Interface is very user friendly to keep you engage. Thanks for your contributions.

Try it 

Try running the script with any input:



example: 2, 3, 7, 3, 9, 8, 2, 8, 9

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2024-08-19
use utf8;     # Week 283 - task 1 - Unique number
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';

unique_number(1, 1, 2, 3, 4, 5, 3, 4, 5);
unique_number(3, 2, 4, 2, 4);
unique_number(7);
unique_number(4, 3, 1, 1, 4);
unique_number(123, 456, 77, 9999, 55, 55, 9999, 77, 456);

sub unique_number {
    
    my (@ints, %freq, $j);
    
    @ints = @_;
    say qq[\nInput:  \@ints = (] . join(', ', @ints) . ')';
    
    # count occurrences of each number
    $freq{$_} ++ for @ints;
    
    # find the (first) one that only occurs once
    for $j (keys %freq) {
        if ($freq{$j} == 1) {
            say qq[Output: $j];
            return;
        }
    }
    
    # none occurs once
    say qq[Output: none];
}

10 lines of code

Output from script


Input:  @ints = (1, 1, 2, 3, 4, 5, 3, 4, 5)
Output: 2

Input:  @ints = (3, 2, 4, 2, 4)
Output: 3

Input:  @ints = (7)
Output: 7

Input:  @ints = (4, 3, 1, 1, 4)
Output: 3

Input:  @ints = (123, 456, 77, 9999, 55, 55, 9999, 77, 
   456)
Output: 123

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain