Language phenotypes: Python, C# - Part 2
Doing a input/supply check function on coderpad.io which supports 30+ languages, but in actuality a language is only as good as its tools and libraries and in that respect coderpad.io is sorely lacking on many of the languages it supports. In truth coderpad.io is built for Python, Javascript, and Ruby other languages are in second tier support.
Unfortunately I chose to use a second tier language (C#) to complete an assignment during an interview and while I got it working I could feel the environment working against me. I really hate that coderpad has no code completion (well it seems it does, but it's slow and you can't explore off of it and it has no interactive debugger)
Here is the problem: Test problem from Guideline
Returns true if the supply collection contains all of the elements in the input collection
So in C# that function signature looks like
public static bool ContainsAll(IList<string?> inputs, IList<string?> supply)
and in Python it would look like
def contains_all(inputs: list, supply: list) -> bool:
Here is my quick and dirty C# implementation, construct some Dictionary<string, int>
and then do set comparison. Unfortunately null
is coming up to bite you on this so you got to do some cleaning. Anyway threw this together and it worked with time, but it was weird and kind of painful.
using System;
using System.Reflection;
using NUnit.Framework;
using NUnitLite;
class Solution
{
/// <summary>
/// Returns true if the supply collection contains all of the elements in the input collection
public static bool ContainsAll(IList<string?> inputs, IList<string?> supply)
{
if (inputs.Count == 0) {
return true;
}
var cleanedLists = new {
Inputs = inputs.Where(z => z != null),
InputsNullCount = inputs.Where(z => z == null),
Supply = supply.Where(z => z != null),
SupplyNullCount = supply.Where(z => z == null)
};
var countInputs = new Dictionary<string, int>();
foreach (var input in cleanedLists.Inputs)
{
if (countInputs.ContainsKey(input))
{
countInputs[input] += 1;
continue;
}
countInputs[input] = 1;
}
var supplyInputs = new Dictionary<string, int>();
foreach (var supplyInput in cleanedLists.Supply)
{
if (supplyInputs.ContainsKey(supplyInput))
{
supplyInputs[supplyInput] += 1;
continue;
}
supplyInputs[supplyInput] = 1;
}
var matches = 0;
foreach (var countInput in countInputs)
{
var inputMatch = countInput.Value;
if (supplyInputs.ContainsKey(countInput.Key))
{
var supplyMatch = supplyInputs[countInput.Key];
if (supplyMatch >= inputMatch)
{
matches++;
}
}
}
var nullCountMatches = (cleanedLists.InputsNullCount != cleanedLists.SupplyNullCount);
/*
Console.WriteLine($"{nullCountMatches}");
Console.WriteLine($"{matches}");
Console.WriteLine($"{countInputs.Keys.Count()}");
foreach (var countItem in countInputs)
{
Console.WriteLine($"{countItem.Key} {countItem.Value}");
}
*/
return (matches == countInputs.Keys.Count() && nullCountMatches);
}
}
[TestFixture]
class Tests
{
public static int Main(string[] args)
{
return new AutoRun(Assembly.GetCallingAssembly()).Execute(new String[] { "--labels=All" });
}
[Test]
public void shouldReturnTrueForAnEmptySetOfInputs()
{
String?[] inputs = new String[0];
String?[] supply = { "a", "b", "c" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfAllInputsAreInTheSupply()
{
String?[] inputs = { "a" };
String?[] supply = { "a" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnFalseIfNotAllTheInputsAreInTheSupply()
{
String?[] inputs = { "a" };
String?[] supply = { "b" };
Assert.False(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfTheInputsAreInADifferentOrderButAreAllPresentInTheSupply()
{
String[] inputs = { "a", "b", "c" };
String[] supply = { "b", "a", "c" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnFalseIfTheSupplyIsSmallerThanTheInputs()
{
String?[] inputs = { "a", "b", "c" };
String?[] supply = { "a", "b" };
Assert.False(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfSupplyContainsExtraValues()
{
String?[] inputs = { "a", "b", "c" };
String?[] supply = { "b", "q", "r", "n", "a", "c" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnFalseIfTheSupplyIsMissingOneNeedle()
{
String?[] inputs = { "a", "b", "c" };
String?[] supply = { "q", "r", "n", "a", "c" };
Assert.False(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnFalseIfNotAllIndividualInputsAreRepresentedInTheSupply()
{
String?[] inputs = { "a", "a", "b", "c" };
String?[] supply = { "b", "q", "r", "n", "a", "c" };
Assert.False(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfTheSupplyContainsMoreOfTheSameNeedle()
{
String?[] inputs = { "a", "a", "b", "c" };
String?[] supply = { "b", "a", "a", "a", "c" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfInputOnlyHasNullValues()
{
String?[] inputs = { null };
String?[] supply = new String[0];
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfSupplyOnlyHasNullValues()
{
String?[] inputs = new String[0];
String?[] supply = { null };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfTheyBothHaveOnlyNullValues()
{
String?[] inputs = { null };
String?[] supply = { null };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfNonNullValuesMatch()
{
String?[] inputs = { null, "a", null };
String?[] supply = { null, null, "a" };
Assert.True(Solution.ContainsAll(inputs, supply));
}
[Test]
public void shouldReturnTrueIfNonNullValuesDontMatch()
{
String?[] inputs = { null, "a", null, "b" };
String?[] supply = { null, null, "a" };
Assert.False(Solution.ContainsAll(inputs, supply));
}
}
Now in python
from collections import Counter
def contains_all(inputs: list, supply: list) -> bool:
if len(inputs) == 0:
return True
if len(supply) == 0 and len(inputs) == 1 and None in inputs:
return True
to_check = Counter(inputs)
available = Counter(supply)
matches = 0
for check_item in to_check.items():
check_letter, check_count = check_item
available_count = available[check_letter]
matches += 1 if available_count >= check_count else 0
return matches == len(to_check.items())
if __name__ == '__main__':
print(contains_all([], ['a', 'b', 'c']) == True)
print(contains_all(['a'], ['a']) == True)
print(contains_all(['a'], ['b']) == False)
print(contains_all(['a', 'b', 'c'], ['b', 'a', 'c']) == True)
print(contains_all(['a', 'b', 'c'], ['a', 'b']) == False)
print(contains_all(['a', 'b', 'c'], ['b', 'q', 'r', 'n', 'a', 'c']) == True)
print(contains_all(['a', 'b', 'c'], ['q', 'r', 'n', 'a', 'c']) == False)
print(contains_all(['a', 'a', 'b', 'c'], ['b', 'q', 'r', 'n', 'a', 'c']) == False)
print(contains_all(['a', 'a', 'b', 'c'], ['b', 'a', 'a', 'a', 'c']) == True)
print(contains_all([None], []) == True)
print(contains_all([], [None]) == True)
print(contains_all([None, 'a', None], [None, None, 'a']) == True)
print(contains_all([None, 'a', None, 'b'], [None, None, 'a']) == False)
Python wins again