Once you have created your data.table, there is little need for the regular assign operator `<-`, instead you want to use `:=`, and this goes **inside** of the brackets in the `j` location. 
(the reason for avoiding `<-` is that `<-` creates a copy of the object, whereas `:=` does not, hence the efficiency)

So first modification to your code would be: 

     # FROM: A.DT[j,]$a2 <- B.DT[i,]$b1
     # TO: 
     A.DT[j, a2 := B.DT[i, b1] ]

---

Now, one of `data.table`'s (many) best features is it's `by` argument, which helps do away with a lot of `for` loops and `*ply` calls. 
In this specific case, you can clean up your dual loops as follows: 


    set.seed(201)
    A.DT <- data.table(a1 = rnorm(N,0,1), key="a1")  # no need to create a2 if it will be NA. If you do, make sure it is as.numeric(NA)
    B.DT <- data.table(b1 = rnorm(N,0,1), b2 = 1:N, key="b2")

    # Assign to a2 in A.DT
    A.DT[            
          , a2 := B.DT[ b2 <= N/2 & b1 < a1] [1, b1]
          , by=a1
         ]


    > A.DT
                 a1         a2
     1: -2.30403431         NA
     2: -1.69658097         NA
     3: -1.28548252         NA
     4: -0.34454603 -0.6478531
     5: -0.07503189 -0.6478531
     6:  0.05593404 -0.6478531
     7:  0.18900414 -0.6478531
     8:  0.26693735  0.2238094
     9:  0.28606069  0.2238094
    10:  0.32576373  0.2238094


----

### Two Sidenote on `key`s. 

   * you can set the key at the same time as you are creating the data.table, saving you two lines of code
   * a data.table is sorted by its key.  Judging by the fact that you are using row position to determine assignment, I am guessing that you will not want to set the keys as you have.  In the code above, I changed `B.DT`'s key to `b2.       


