## DEV Community is a community of 603,444 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

# Advent of Code 2020-07 with R & Neo4j

Colin Fay Originally published at colinfay.me on ・3 min read

Solving Advent of Code 2020-07 with R & Neo4j.

[Disclaimer] Obviously, this post contains a big spoiler about Advent of Code.

## R & Neo4j solution

As today’s options was a graph challenge, I decided to play a little bit with R and Neo4J.

### Part one

``````library(tidyverse)

## ── Attaching packages ───────────── tidyverse 1.3.0 ──

## ✓ ggplot2 3.3.0 ✓ purrr 0.3.4
## ✓ tibble 3.0.1 ✓ dplyr 1.0.2
## ✓ tidyr 1.0.3 ✓ stringr 1.4.0
## ✓ readr 1.3.1 ✓ forcats 0.5.0

## ── Conflicts ──────────────── tidyverse_conflicts() ──

"2020-07-aoc.txt",
stringsAsFactors = FALSE
) %>% as_tibble()

clean <- input %>%
# Extracting the origin
mutate(
origin = gsub("(.*) bags .*", "\\1", V1),
contain = gsub(".* contain (.*) bags*\\.", "\\1", V1),
contain = map(contain, ~ (strsplit(.x, ","))[[1]])
) %>%
unnest(contain) %>%
# Extracting what they can contain
mutate(
n_contain = gsub(".*([0-9]+).*", "\\1", contain),
n_contain = case_when(
n_contain == "no other" ~ "0",
TRUE ~ n_contain
) %>% as.character(),
contain = gsub(" bags*", "", contain),
contain = gsub("[0-9]*", "", contain),
contain = stringr::str_trim(contain)
) %>%
# Keeping only the needed elements
select(
origin, contain, n_contain
)
clean

## # A tibble: 1,395 x 3
## origin contain n_contain
## <chr> <chr> <chr>
## 1 clear maroon dull lavender 1
## 2 wavy turquoise vibrant magenta 4
## 3 wavy turquoise light violet 4
## 4 wavy turquoise bright gold 5
## 5 wavy turquoise faded black 2
## 6 wavy beige plaid magenta 3
## 7 wavy beige wavy lime 3
## 8 wavy beige clear turquoise 2
## 9 wavy beige muted cyan 3
## 10 mirrored black plaid red 1
## # … with 1,385 more rows

# Launching the container

system("docker run --rm --name neo4j --env NEO4J_AUTH=neo4j/password --publish=7687:7687 --publish=7474:7474 -v \$(pwd):/var/lib/neo4j/import -d neo4j:3.4")

library(neo4r)
# Connecting to the Neo4j API
con <- neo4j_api\$new(
url = "http://localhost:7474",
user = "neo4j",
)

# Waiting for the container to be ready
Sys.sleep(10)
while (try(con\$ping()) != 200){
Sys.sleep(10)
}

# Writing CSV for Neo4J
data.frame(
nodes = unique(clean\$origin, clean\$contain)
) %>%
write_csv("aoc07_nodes.csv")
clean %>%
write_csv("aoc07_relationships.csv")

# Adding constraint of uniqueness on names
"CREATE CONSTRAINT ON (origin:origin) ASSERT origin.name IS UNIQUE;" %>%
call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 0
## 5 relationships_created 0
## 6 relationship_deleted 0
## 8 labels_removed 0
## 10 indexes_removed 0
## 12 constraints_removed 0

"CREATE CONSTRAINT ON (contain:contain) ASSERT contain.name IS UNIQUE;" %>%
call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 0
## 5 relationships_created 0
## 6 relationship_deleted 0
## 8 labels_removed 0
## 10 indexes_removed 0
## 12 constraints_removed 0

MERGE (:bag {name : csvLine.nodes} ) ' %>%
call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 2 nodes_created 594
## 3 nodes_deleted 0
## 4 properties_set 594
## 5 relationships_created 0
## 6 relationship_deleted 0
## 8 labels_removed 0
## 10 indexes_removed 0
## 12 constraints_removed 0

MATCH (c:bag {name : csvLine.contain})
MATCH (o:bag {name : csvLine.origin})
MERGE (o) -[:CAN_CONTAIN {n_contain: csvLine.n_contain}]->(c);' %>%
call_neo4j(con, include_stats = TRUE)

## No data returned.

## # A tibble: 12 x 2
## type value
## <chr> <dbl>
## 2 nodes_created 0
## 3 nodes_deleted 0
## 4 properties_set 1385
## 5 relationships_created 1385
## 6 relationship_deleted 0
## 8 labels_removed 0
## 10 indexes_removed 0
## 12 constraints_removed 0

# Count all the bags that can eventually contain a shiny gold ones
'MATCH (b:bag)-[:CAN_CONTAIN*1..]-> (:bag {name:"shiny gold"}) RETURN count( DISTINCT b) AS count' %>%
call_neo4j(con)

## \$count
## # A tibble: 1 x 1
## value
## <int>
## 1 126
##
## attr(,"class")
## [1] "neo" "list"
``````

### Part two

``````'MATCH (:bag {name:"shiny gold"})-[r:CAN_CONTAIN*1..]-> (:bag) WITH reduce(tot = 1, i IN r| tot * toInteger(i.n_contain)) AS total RETURN sum(total) AS total' %>%
call_neo4j(con)

## \$total
## # A tibble: 1 x 1
## value
## <int>
## 1 220149
##
## attr(,"class")
## [1] "neo" "list"

# Removing the container
system("docker kill neo4j")