How to match by nearest date from two data frames?

Suppose I have two data frames such like:

set.seed(123)
df1<-data.frame(bmi=rnorm(20, 25, 5),
                date1=sample(seq.Date(as.Date("2014-01-01"),
                             as.Date("2014-02-28"),by="day"), 20))

df2<-data.frame(epi=1:5, 
                date2=as.Date(c("2014-1-8", "2014-1-15", "2014-1-28", 
                                "2014-2-05", "2014-2-24")))

My question is how to match bmi with epi where date1 are nearest before or on the date2? A result such like this:

  epi      date2   bmi      date1
1   1 2014-01-08 33.58 2014-01-08
2   2 2014-01-15 22.64 2014-01-15
3   3 2014-01-28 22.22 2014-01-26
4   4 2014-02-05 15.17 2014-02-01
5   5 2014-02-24 27.49 2014-02-15

One way is to use the roll=Inf feature from the data.table package as follows:

require(data.table)   ## >= 1.9.2
setDT(df1)            ## convert to data.table by reference
setDT(df2)            ## same

df1[, date := date1]  ## create a duplicate of 'date1'
setkey(df1, date1)    ## set the column to perform the join on
setkey(df2, date2)    ## same as above

ans = df1[df2, roll=Inf] ## perform rolling join

## change names and set column order as required, by reference
setnames(ans, c('date','date1'), c('date1','date2'))
setcolorder(ans, c('epi', 'date1', 'bmi', 'date2'))

> ans
#   epi      date1      bmi      date2
#1:   1 2014-01-08 33.57532 2014-01-08
#2:   2 2014-01-15 22.63604 2014-01-15
#3:   3 2014-01-26 22.22079 2014-01-28
#4:   4 2014-02-01 15.16691 2014-02-05
#5:   5 2014-02-15 27.48925 2014-02-24

Here a way with base R

# get time differences
temp <- outer(df2$date2, df1$date1,  "-")

# remove where date1 are after date2
temp[temp < 0] <- NA

# find index of minimum
ind <- apply(temp, 1, function(i) which.min(i))

# output
df2 <- cbind(df2,  df1[ind,])

An alternative based on finding the index of the closest date

library(tidyverse)
# Function to get the index specifying closest or after
Ind_closest_or_after <- function(d1, d2){
  which.min(ifelse(d1 - d2 < 0, Inf, d1 - d2))
}

# Calculate the indices
closest_or_after_ind <- map_int(.x = df2$date2, .f = Ind_closest_or_after, d2 = df1$date1)

# Add index columns to the data frames and join
df1 <- df1 %>% 
  mutate(ind = 1:nrow(df1))

df2 <- df2 %>% 
  mutate(ind = closest_or_after_ind)

left_join(df2, df1, by = 'ind')

Also check survival::neardate