There's probably a more elegant solution, but here is one way to approach it. The idea is move df2 into long format and then join it onto a long format data.frame that contains all possible fruits for each row. Convert the missing values to zero. Then shift the data from long format back to wide.
# assemble df2
df2 <- structure(list(fruit1 = structure(c(1L, 3L, 2L),
.Label = c("apple", "banana", "grape"),
class = "factor"),
count1 = c(2L, 4L, 1L),
fruit2 = structure(c(2L, 1L, NA),
.Label = c("orange", "pear"),
class = "factor"),
count2 = c(1L, 2L, NA)),
.Names = c("fruit1", "count1", "fruit2", "count2"),
class = "data.frame", row.names = c(NA, -3L))
# add a column for row numbers
df2$r_number <- 1:nrow(df2)
# load libraries
library(tidyr)
library(dplyr)
# assemble a data.frame in long form by row numbers and fruits
fruit <- df2 %>%
select(starts_with("fruit"), r_number) %>%
gather(condition, fruits, starts_with("fruit")) %>%
mutate(condition = gsub("fruit", "key", condition)) %>%
filter(!is.na(fruits))
# assemble a data.frame in long form by row numbers and counts
count <- df2 %>%
select(starts_with("count"), r_number) %>%
gather(condition, counts, starts_with("count")) %>%
mutate(condition = gsub("count", "key", condition),
counts = ifelse(is.na(counts), 0, counts))
# join counts onto fruits
fruit %<>%
left_join(count, by = c("condition", "r_number")) %>%
mutate(fruits = paste0(fruits, "s")) %>%
select(-condition)
# create data.frame for all possible row numbers and fruits
all_possibles <- data.frame( r_number = rep(c(1:nrow(df2)), length(unique(fruit$fruits))),
fruits = unlist(lapply(unique(fruit$fruits), function(x) rep(x, nrow(df2)))))
# join actual fruits/counts onto all possible fruits and counts
# and shift data from long to wide
output <- all_possibles %>%
left_join(fruit, by = c("r_number", "fruits")) %>%
mutate(counts = ifelse(is.na(counts), 0, counts)) %>%
spread(fruits, counts) %>%
select(-r_number)