Solving a Code Wars Challenge with the Reduce Method and a Bitwise Operator

Filip MelkaFilip Melka
5 min read

While solving problems on Code Wars, I came across an interesting problem called Find the Odd Int. It seemed simple at first, but one particular solution fascinated me so much that I decided to write about it.

The problem

Here's what the problem asks you to do:

๐Ÿ’ก
Given an array of integers, find the one that appears an odd number of times. There will always be only one integer that appears an odd number of times.

For example:

  • [1,1,2] โ†’ return 2

  • [1,3,1,3,1] โ†’ return 1

The solution

My solution used an object to count the occurrences of each number, but I stumbled upon a more elegant solution other programmers came up with:

function findOdd(arr) {
    return arr.reduce((accumulator, currentValue) => accumulator ^ currentValue)
}

As a beginner, it took me some time to understand this concise function. Let's break it down.

The reduce() method

Let's break the function down into individual components, starting with the reduce() method.

The reduce() method can be called on an array. It executes a reducer callback function on each element of the array. The value the callback function returns is then passed into the callback function again on the next invocation as accumulator. After it processes all elements of the array, it returns a single value (the accumulated result). To understand the reduce() method better, let's use it to sum all the numbers in an array (which is a common use case of this method). Here's an example of how to do so:

const arr = [1, 2, 3, 4]

// (0) + 1 + 2 + 3 + 4
const sum = arr.reduce(
    (accumulator, currentValue) => accumulator + currentValue,
    0
)
// sum = 10

The reduce() method iterates over all numbers in the array. On each iteration, two variables (accumulator and currentValue) are passed to the callback function. The currentValue stores the current number of the array and the accumulator variable stores the accumulated result, which is then returned by the reduce() method after we iterate over all elements of the array. Because we have passed a 0 as the second argument to the reduce() method, the accumulator is initially set to 0.

If we don't specify the initial value, the array element at index 0 is used as the initial value and we start the iteration from the second element (at index 1). Here's the same example without the initial value:

const arr = [1, 2, 3, 4]

// 1 + 2 + 3 + 4
const sum = arr.reduce(
    (accumulator, currentValue) => accumulator + currentValue
)
// sum = 10

The XOR (^) operator

XOR (exclusive or) is a bitwise operator that returns 0 (false) if two bits are the same and 1 (true) if they are different. In JavaScript, the syntax for XOR is ^, while in mathematics, the โŠ• symbol is commonly used.

Let's look at an example. We will 'XOR' numbers 2 and 7.

console.log(2 ^ 7) // 5

The above code will print out 5, because if we write down the numbers in binary and perform the XOR operation on each pair of bits, we will get 101, which is 5 in decimal.

It's important to note, that if we use the XOR operator on two same numbers, the result is 0.

And if we use the XOR on any number \(x\) and 0, the result is \(x\). For example, if we "XOR" numbers 2 and 0, we get 2.

Another important property of the XOR operation is that it is associative and commutative. This simply means that the order of input values and how they're grouped doesn't affect the final output. For example, (1 โŠ• 2) โŠ• 3 gives us the same result as 2 โŠ• (1 โŠ• 3).

Putting things together

Let's put everything together and walk through the execution of the findOdd() function. We were given an array with these values: [1,2,1]. We will apply the reduce() method on this array. Because we don't pass in the initial value, the first element is used as the initial value (we will set the accumulator to 1) and start iterating from the second element.

During the first iteration, the accumulator is set to 1 and the currentValue is set to 2. The callback function returns 3 (1 โŠ• 2 or in binary 01 โŠ• 10).

In the next iteration, the accumulator is set to 3 and the currentValue is set to 1. The callback function returns 2 (3 โŠ• 1 or in binary 11 โŠ• 01).

Because there are no more elements in the array, the reduce() method returns the accumulated result, which in our case is 2.

If we put the syntax aside, all the findOdd() function does is 'XORing' all numbers in the given array and returning the result.

But how does 'XORing' all numbers give us the correct answer to our problem? Well, because we said that the order and grouping of the input values doesn't matter, we can rewrite it as:

'XORing' same numbers results in 0. This also applies to 'XORing' the same number an even number of times. Because of that, all numbers that occur an even number of times in our array will result in 0:

We are left with the number that occurs an odd number of times (and as the problem instructions state, such a number is only one in the array).

On the other hand, when we 'XOR' the same number \(x\) an odd number of times, the result is \(x\). Here is an example to showcase that:

So by 'XORing' all numbers of the array (using the reduce() method) and returning the result, we get the answer to our problem. This method works for all arrays that satisfy the problem's description - here are some more examples:

Summary

In this article, we discussed the reduce() method and the XOR operator, focusing on its key properties:

  • x โŠ• x = 0

  • x โŠ• 0 = x

  • Associative and commutative properties allow rearranging and grouping without affecting the result.

We then applied these concepts to solve a programming problem. This solution is not only concise but also highlights the powerful and elegant use of bitwise operations in programming.


Sources๐Ÿ”—

0
Subscribe to my newsletter

Read articles from Filip Melka directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Filip Melka
Filip Melka

Hi there!๐Ÿ‘‹ Iโ€™m Filip, an enthusiastic software development student๐Ÿ‘จโ€๐ŸŽ“ My passion for learning led me to pursue a path in software development. I believe software has the incredible potential to revolutionize the learning experience, making it more accessible, engaging, and rewarding. With the rapid advancements in AI and other technologies, I'm excited about the potential to make a positive impact in education, and Iโ€™m eager to be a part of this transformation. Why did I start this blog? Mainly for two reasons. First, it keeps me motivated. As an aspiring software developer, I encounter ๐Ÿž every day and sometimes feel like giving up. Writing about my side projects and what Iโ€™m learning keeps me going๐Ÿ’ช Second, writing helps me organize my thoughts and grasp complex topics more thoroughly. If my posts help someone else too, thatโ€™s a huge win!