How to Split an Array Into Odd Pairs (An Interview Exercise)

Here’s an exercise that I may or may not have found… or been asked to solve.

Off the top of my head, the exercise might or might not have looked like this,

Let’s start with an array of integers of even size. Return if the array can be divided into N/2 pairs where each pair adds up to an odd number and those N/2 pairs contain every element of the given array.

For example, [1, 2, 3, 4] passes those two conditions. Here are its pairs: (1, 2) and (3, 4). Their sum is an odd number.

But [1, 3, 5, 7] doesn’t pass our two conditions. Each pair adds up to an even number.

Cheating with StackOverflow and Copilot

Let’s create a producer-like method to generate all pairs, and then filter them out based on our two conditions.

I’d like to write something like this,

bool HasFunnyPairs(int[] array)
    => array.Pais()
        .Where(pair => IsOdd(Sum(pair)))
        .Combinations(array.Length / 2)
        .Any(combination => IsEachElementInExactlyOnePair(array, combination));

First, generate all pairs with an odd sum, then form N/2-size combinations, filtering out those that don’t include every element of the array.

Since, we’re already in the future and I’m not in front of a whiteboard and an interviewer, I’m cheating with StackOverflow and Copilot.

Generating k-Combinations

Here’s a StackOverflow answer with a recursive method in JavaScript to choose k-combinations of an array,

function choose(arr, k, prefix=[]) {
    if (k == 0) return [prefix];
    return arr.flatMap((v, i) =>
        choose(arr.slice(i+1), k-1, [...prefix, v])
    );
}

I asked Copilot to translate it to C#. After some tweaking, we have this,

static IEnumerable<IEnumerable<T>> Combinations<T>(IEnumerable<T> source, int size)
{
	return CombinationsInternal(source, size, null);

	static IEnumerable<IEnumerable<T>> CombinationsInternal(IEnumerable<T> source, int size, List<T>? prefix = null)
	{
		prefix ??= [];

		if (size == 0)
		{
			yield return new List<T>(prefix);
			yield break;
		}

		var length = source.Count();
		foreach ((T element, int position) in source.Select((element, position) => (element, position)))
		{
			List<T> newPrefix = [.. prefix, element];
			foreach (var combination in CombinationsInternal([.. source.Skip(position + 1)], size - 1, newPrefix))
			{
				yield return combination;
			}
		}
	}
}

With that method, we can write Pairs() to generate a list of tuples, representing each pair,

static IEnumerable<(int First, int Second)> Pairs(this int[] array)
    => array.Combinations(2)
            .Select(pair => (First: pair.First(), Second: pair.Last()));

Finding if each element is present in only one pair

The methods IsOdd() and Sum() are easy. The one left is IsEachElementInExactlyOnePair(). Copilot to the rescue again,

static bool IsEachElementInExactlyOnePair(int[] elements, IEnumerable<(int First, int Second)> pairs)
{
    var elementCount = new Dictionary<int, int>();

    // Count occurrences of elements in pairs
    foreach (var (First, Second) in pairs)
    {
        elementCount[First] = elementCount.GetValueOrDefault(First) + 1;
        elementCount[Second] = elementCount.GetValueOrDefault(Second) + 1;
    }

    // Check if each element appears exactly once
    return elements.All(e => elementCount.GetValueOrDefault(e) == 1)
           && elements.All(e => elementCount.ContainsKey(e));
}

Now, after some gluing and turning some of those methods into extension methods, here’s the final solution,

On a Helpers.cs file,

namespace HereIGo;

public static class Helpers
{
    public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> array, int size)
    {
        return CombinationsInternal(array, size, null);

        static IEnumerable<IEnumerable<T>> CombinationsInternal(IEnumerable<T> array, int size, List<T>? prefix = null)
        {
            prefix ??= [];

            if (size == 0)
            {
                yield return new List<T>(prefix);
                yield break;
            }

            var length = array.Count();
            foreach ((T element, int position) in array.Select((element, position) => (element, position)))
            {
                List<T> newPrefix = [.. prefix, element];
                foreach (var combination in CombinationsInternal([.. array.Skip(position + 1)], size - 1, newPrefix))
                {
                    yield return combination;
                }
            }
        }
    }

    public static IEnumerable<(int First, int Second)> Pairs(this int[] array)
        => array.Combinations(2)
            .Select(pair => (First: pair.First(), Second: pair.Last()));

    public static int Sum(this (int First, int Second) pair) => pair.First + pair.Second;

    public static bool IsOdd(this int n) => n % 2 != 0;

    public static bool IsEachElementInExactlyOnePair(this int[] elements, IEnumerable<(int First, int Second)> pairs)
    {
        var elementCount = new Dictionary<int, int>();

        // Count occurrences of elements in pairs
        foreach (var (First, Second) in pairs)
        {
            elementCount[First] = elementCount.GetValueOrDefault(First) + 1;
            elementCount[Second] = elementCount.GetValueOrDefault(Second) + 1;
        }

        // Check if each element appears exactly once
        return elements.All(e => elementCount.GetValueOrDefault(e) == 1)
               && elements.All(e => elementCount.ContainsKey(e));
    }
}

On a Main.cs file,

using HereIGo;

static bool OddPairs(int[] array)
    => array.Pairs()
        .Where(p => p.Sum().IsOdd())
        .Combinations(array.Length / 2)
        .Any(combination => array.AllExactlyInOnePair(combination));

OddPairs([1, 2, 3, 4]); // true
OddPairs([1, 3, 5, 7]); // false
OddPairs([1, -1]); // false
OddPairs([2, -1]); // true

AI has changed the the tech interview landscape for good. The other day, I asked AI to solve another interview exercise. And I was surprised. And more recently, I’ve used Copilot to help me with these 4 boring coding tasks. Yes, AI isn’t going anywhere. So we as coders have to adapt, like we have always done.

The Most Challenging Managers to Work With (And What to Do About It)

I’ve changed my mind about what a good manager is.

At one past job, I worked on short-term projects. Every six months or so, I had new projects with a new team leader and sometimes a new team.

I worked with all kinds of managers. From those who still coded to others who hadn’t touched code in years.

The most challenging ones? The newly promoted team members.

In times of uncertainty, which is pretty much all the time, they fell back on what they know best: coding. And they forgot that as managers, they aren’t the best coders anymore.

In those situations, the best thing to do is follow the advice from Why Engineers Hate Their Managers:

If you’re an engineer frustrated with your manager, consider that they might be drowning too—sometimes the best thing you can do is have an honest conversation about what you both need to succeed.

The day before, they were just teammates who got a quick promotion, tapped on their shoulder with no training or clear expectations. Just, “You’re a manager now. Figure it out. See you at the next performance review.”

I used to think a good manager was a still-technical team member making code-level decisions. Now, a good manager is simply someone I’d like to work with again.

Remix Your Best Content Ideas—Like a Hit Song

If you’re like me, you’ve listened to your favorite songs plenty of times.

Not just often, but over and over, in different versions. A live version during a concert, a studio version with a string quartet, or a duo version with another well-known artist.

If singers do it, why not do the same thing in our content?

A good post can become an email for your newsletter or part of a book chapter. The message stays the same, but presented in a new format for a new audience. And that makes it different.

As Robert Birming put it on Blog Recycling,

Dig up an old post and rewrite it from your current perspective. It’s not cheating, and it’s not copying. It’s growth.

It’s like singers giving a popular song a fresh arrangements, just like we can to our best posts.

5 Reasons Why Listicles Rule Online (And Crush It on Social Media)

#1. They offer a clear promise. “10 reasons” or “5 mistakes” are way better than “on writing” or “some thoughts on blogging.” It’s clear what to expect from the first two.

#2. They are easy to digest and skim. People don’t read online. They skim. A listicle with bolded bullet points or subheaders is easier to skim than text-heavy essays. People can jump in at any point after skimming. And even if they only read those subheaders, they will take away some value from a listicle.

#3. They follow a proven structure. Listicles are popular because they work. The 10 Commandments, The 48 Laws of Power, and 21 Lessons for the 21st Century are just three examples of listicles.

#4. They offer more interaction points. Every point invites comments and conversation. “I really like point #n…“

#5. They offer more repurposing opportunities. Listicle points with the most interaction make great standalone posts. See #4. That’s one tip to double our post count.

Best of all? If you already write 10 ideas a day, listicles practically write themselves.

I've Replaced My Second Brain With a Simpler Method

For a long time, I was a note-taking freak.

I spent hours trying to find the perfect note-taking system. I tried a bullet journal, a simple A-4 sheet of paper folded twice, and an endless .txt file. I probably have an old comparison table of note-taking buried in my old computer.

Starting a second brain

Years later, probably scrolling on Hacker News, I found out about the Zettelkasten method.

It fascinated me so much that I read How to Take Smart Notes to learn more about the method and started my own “zettel.” But recently I realized that I barely revisit most of my notes. I’m not interested in the same topics anymore.

Simply writing 10 ideas

And after reading some of James Altucher’s books and recovering from burnout, I’ve changed my mind about productivity and to-do lists.

Instead of to-do lists and goals, I’ve started to write 10 ideas a day to become an idea machine. It has changed my note-taking habits too. Now, after every book, podcast, or course, I write down 10 key lessons. It forces me to recall the main ideas from the content I consume. I don’t need to write every single detail but rather remember the 10 points that resonate the most. That effort makes the learning stick.

I still write down the quotes I like the most from books I read. But I’ve stopped chasing second-brain systems and testing every new AI-powered app. I simply write 10 ideas. It’s simple and more useful than any system I’ve tried.