2018 Advent of Code Day 02 Part B

purrr
tidyverse

#1

I need some help trying to come up with a real functional solution to the below problem

The boxes will have IDs which differ by exactly one character at the same position in both strings. For example, given the following box IDs:

abcde
fghij
klmno
pqrst
fguij
axcye
wvxyz

The IDs abcde and axcye are close, but they differ by two characters (the second and fourth). However, the IDs fghij and fguij differ by exactly one character, the third ( h and u ). Those must be the correct boxes.

What letters are common between the two correct box IDs? (In the example above, this is found by removing the differing character from either ID, producing fgij .)

So far my code is as follows (leeched from github user wittmaan)

input <- c("abcde", "fghij", "klmno", "pqrst", "fguij", "axcye", "wvxyz")
input <- strsplit(sort(input), "")

for (i in 2:length(input)) {
  ident <- input[[i-1]] == input[[i]]
  equal_char <- sum(ident)
  if (length(input[[i]]) - 1 == equal_char) {
    print(paste0(input[[i]][ident], collapse = ""))
  }
}

Which works but I believe I can approach this problem in a more functional manner. I am thinking it would be possible to use map() inside of a mutate to iterate through each value in the list.


#2

I wrote a function check_for_one_character_neighbors(strings, position) that drops all the characters at character position position in a vectors of strings strings, and it returns any repeated elements. I used purrr to map over all the lengths.

seq_len(nchar(input[1])) %>%
  purrr::map(~ check_for_one_character_neighbors(input, .x)) %>%
  purrr::flatten_chr()

The :bulb: trick here to make this more functional and mappable is that I used a formula to partially apply an argument to a function. In this case, I fixed the strings argument to be the same set of strings input. As a result, map() maps over the possible character positions.

Another way to write this solution would be to explicitly partially apply the strings before mapping.

check_these_strings <- function(position) {
  check_for_one_character_neighbors(strings = input, .x)
}

seq_len(nchar(strings[1])) %>%
  purrr::map(check_these_strings) %>%
  purrr::flatten_chr()

So! The larger functional programming concepts to remember from this example:

  • defining one-off functions that fix certain function arguments to a default value. This is partial application.
  • defining one-off functions to reorder arguments
  • using these techniques in order to create functions that can feed more neatly into map()