Stuart Breckenridge

Extending Array to Enforce Unique Elements

On the subject of our inability to pick random numbers, Keith Hillman writes1:

You might think that you know how to choose a random number but in all likelihood you are probably falling for a number of common mistakes that are giving the game away.

Software can solve this issue. I’m going to write a function that generates six random numbers for my next lottery ticket and will then store those numbers in an Array. The problem is that I can’t stop the random number function from generating duplicates and, out of the box, an Array will not stop duplicate values from being appended. Let’s have a look at how to enforce uniqueness in an Array.

First, let’s see what the basic code looks like before the problem is solved.

var numbers = [Int]() 

while numbers.count < 6 {
    numbers.append((Int(rand() % 49)) + 1) 
}

If you run this a few times you’ll begin to see duplicates appear in the numbers array.

[29, 1, 15, 12, 15, 17]

To stop that from happening you could generate your numbers, add them to a Set, check it has a count of six and then add them to an Array, or you could iterate over each number in the array and compare it to every other number in the array, or as we’re about to do, you could extend the array to include a new appendIfUnique function that can be reused easily.

extension Array where Element:Equatable 
{
    mutating func appendIfUnique(newElement:Element){
        var unique:Bool = true
        for item in self where newElement == item 
        {
            unique = false
            break
        }
        if unique
        {
            self.append(newElement)
        }
    }
}

What we are doing here is extending Array with new functionality in situations where the Elements stored in the array conform to the Equatable2 protocol. Types that conform to Equatable can be compared for value equality using == and !=.

The function is declared as mutating because we are modifying the array from within the function. A boolean—unique—is used to track the result from the equality test. Looping through the array with the for statement, the unique boolean will be changed to false when the element to be added has the same value as the the element being checked, at which point the for loop will break. Finally, if unique remains true at the end of the for loop, the newElement is added to the array.

In use, the revised code is almost identical:

var numbers = [Int]() 

while numbers.count < 6 {
    numbers.appendIfUnique((Int(rand() % 49)) + 1) 
}

You can try this code using the IBM Swift Sandbox.

  1. Why We Can’t Choose Random Numbers, http://www.psychology24.org ↩︎

  2. Equatable Protocol Reference, developer.apple.com ↩︎