 <p><strong>Updated solution using non-equi joins:</strong></p> <p>Recently, <em>non-equi</em> joins were implemented in the <a href="https://github.com/Rdatatable/data.table/wiki/Installation" rel="nofollow">current development version of data.table, v1.9.7</a>. This is quite straightforward with this feature:</p> <pre><code>require(data.table) # v1.9.7+ dt1 = data.table(x=foo) dt2 = data.table(y=foo+10L) dt1[dt2, on=.(x &lt; y), mult="last", which=TRUE] # [1] 4 4 5 5 6 7 8 8 9 </code></pre> <p>On 100,000 elements, this is faster than <code>foverlaps</code>:</p> <pre><code>set.seed(45L) foo &lt;- sort(sample(1e6, 1e5, FALSE)) dt1 = data.table(x=foo) dt2 = data.table(y=foo+10L) system.time(ans &lt;- dt1[dt2, on=.(x &lt; y), mult="last", which=TRUE]) # user system elapsed # 0.011 0.001 0.011 </code></pre> <p>Note that this operation can be done directly as follows:</p> <pre><code>ans &lt;- data.table(x=foo)[.(y=x+10L), on=.(x &lt; y), mult="last", which=TRUE] </code></pre> <hr> <p><strong>Old approach using <code>foverlaps</code>:</strong></p> <p>Here's a method that would probably scale better. Using overlapping range joins function <code>foverlaps()</code> from <code>data.table</code> version 1.9.4:</p> <pre><code>require(data.table) ## 1.9.4+ x = data.table(start=foo, end=foo+9L) lookup = data.table(start=foo, end=foo) setkey(lookup) ## order doesn't change, as 'foo' is already sorted foverlaps(x, lookup, mult="last", which=TRUE) # [1] 4 4 5 5 6 7 8 8 9 </code></pre> <p>Timing on 100,000 numbers:</p> <pre><code>set.seed(45L) foo &lt;- sort(sample(1e6, 1e5, FALSE)) arun &lt;- function(foo) { x = data.table(start=foo, end=foo+9L) lookup = data.table(start=foo, end=foo) setkey(lookup) foverlaps(x, lookup, mult="last", which=TRUE) } system.time(arun(foo)) # user system elapsed # 0.142 0.009 0.153 </code></pre>