Day 6 Part A
Once you decode the problem, it essentially comes down to the number of unique answered questions within a group. Easiest solution (to be of my knowledge here) is to a use a HashSet, which will only allow unique values in.
Following the pattern to an earlier puzzle in the week, essentially a loop to store the answers and then add to the counter when a blank line is found. Also, note to handle the final line which probably isn’t ended with a blank line. Again, might be a nicer way to do this with LINQ.
So onto the Tests (always start with tests! – whether solving problems in spare time, or on paid time):
[TestMethod]
public void ExampleInput()
{
var inputList = new List<string>()
{
"abc", "", "a", "b", "c", "", "ab", "ac", "",
"a", "a", "a", "a", "", "b"
};
int sumOfCounts = new Day6PartA().GetSumOfAnswers(inputList);
Assert.AreEqual(11, sumOfCounts);
}
[TestMethod]
public void FileInput()
{
var filename = @"C:\_path_\Day6\input.txt";
var inputList = File.ReadAllLines(filename).ToList();
int sumOfCounts = new Day6PartA().GetSumOfAnswers(inputList);
Assert.AreEqual(6443, sumOfCounts);
}
The implementation then follows as this:
public int GetSumOfAnswers(List<string> input)
{
int sum = 0;
HashSet<char> charactersFound = new HashSet<char>();
foreach (var line in input)
{
if (line.Trim().Length == 0)
{
// End of processing, so process here:
sum += charactersFound.Count;
charactersFound = new HashSet<char>();
}
else
{
// Need to process this line,
// splitting into characters and then adding to the set
// Set should handle not adding duplicates automatically.
// Could probably do this with LINQ.
for (int i = 0; i < line.Length; i++)
{
charactersFound.Add(line[i]);
}
}
}
// Don't forgot the final line!
sum += charactersFound.Count;
return sum;
}
Day 6 Part B
So Part B is all about finding the consistent entries from the entire group, which makes it a little more interesting. With a few other commitments I quickly churned the following solution out, but it felt a bit over worked and I thought there might be a nicer way.
public int GetAnswersGivenByEveryoneInGroup(List<string> input)
{
int sum = 0;
List<string> tempCollection = new List<string>();
foreach (var line in input)
{
if (line.Trim().Length == 0)
{
if (tempCollection.Count == 1)
{
sum += tempCollection[0].Length;
}
else
{
Dictionary<char, int> countOfChars = new Dictionary<char, int>();
foreach (var tempLine in tempCollection)
{
for (int i = 0; i < tempLine.Length; i++)
{
var currentValue = countOfChars.GetValueOrDefault(tempLine[i], 0);
countOfChars[tempLine[i]] = ++currentValue;
}
}
foreach (var entry in countOfChars)
{
if (entry.Value == tempCollection.Count)
{
sum++;
}
}
}
tempCollection = new List<string>();
}
else
{
tempCollection.Add(line);
}
}
if (tempCollection.Count == 1)
{
sum += tempCollection[0].Length;
}
else
{
Dictionary<char, int> countOfChars = new Dictionary<char, int>();
foreach (var tempLine in tempCollection)
{
for (int i = 0; i < tempLine.Length; i++)
{
var currentValue = countOfChars.GetValueOrDefault(tempLine[i], 0);
countOfChars[tempLine[i]] = ++currentValue;
}
}
foreach (var entry in countOfChars)
{
if (entry.Value == tempCollection.Count)
{
sum++;
}
}
}
return sum;
}
So it works, but there are loops within loops within loops, and its quite length (ok, I could refactor out to helper methods by still).
After a little break I came back to it, and came up with the following solution which only adds consistently appearing characters from each pass, and I think reads a bit better:
public int GetAnswersGivenByEveryoneInGroupRefactor(List<string> input)
{
int sum = 0;
string buildingList = string.Empty;
var isFirstPass = true;
foreach (var line in input)
{
if (line.Trim().Length == 0)
{
sum += buildingList.Length;
buildingList = string.Empty;
isFirstPass = true;
}
else
{
if (isFirstPass)
{
buildingList = line;
isFirstPass = false;
}
else
{
var tempNewList = new StringBuilder();
for (int i = 0; i < line.Length; i++)
{
if (buildingList.Contains(line[i]))
{
tempNewList.Append(line[i]);
}
}
buildingList = tempNewList.ToString();
}
}
}
sum += buildingList.Length;
return sum;
}
But I think this can still be improved on, using something like HashSet or Intersect. Indeed, I carried on for a bit longer and refactored to use LINQ Intersect, which now makes the code read as:
public int GetAnswersGivenByEveryoneInGroupRefactor(List<string> input)
{
int sum = 0;
string buildingList = string.Empty;
var isFirstPass = true;
foreach (var line in input)
{
if (line.Trim().Length == 0)
{
sum += buildingList.Length;
buildingList = string.Empty;
isFirstPass = true;
}
else
{
if (isFirstPass)
{
buildingList = line;
isFirstPass = false;
}
else
{
var buildingListArray = buildingList.ToCharArray();
var lineArray = line.ToCharArray();
var intersect = buildingListArray.Intersect(lineArray).ToArray();
buildingList = String.Join("", intersect);
}
}
}
sum += buildingList.Length;
return sum;
}
I did look at if it would be possible drop the ‘isFirstPass’ boolean, but removing it stopped things from passing. On reflection I suspect its because there is a chance the ‘buildingList’ parameters empties through processing.