Graham Blatz actually talked about just this exact thing at the Advanced Scripting Session at the JMP conference this year.  I have his code from a while ago but he actually improved upon it by using KDTable() which makes it insanely fast. 
 
 
Here's a snippet of the function that does the most important bits.
 
 
dt2 << New Column( "Loc Min", numeric, continuous, formula( Row() ) ); //indexing, basically, to match/update upon
       list_big = Column( dt2, rt2 ) << get values; //creates a vector of values for the following
      
       dt1 << New Column( "Loc Min",
              numeric,
              continuous,
              formula(
                     Loc Min( Abs( Column( rt1 )[Row()] - list_big ) )
              )
       );   //simple, but fast.  Very fast.
      
       dt_join = dt1 << Join(
              With( dt2 ),
              By Matching Columns( :Loc Min = :Loc Min ),
              Drop multiples( 0, 1 ),
              Name( "Include non-matches" )(0, 0),
              Preserve main table order( 1 )
       );  // I opted for a join rather than an update, for now. may change in the future.
Here's the actual function as well that you can call and has a UI with it. 
nearest_neighbor = Function(
       //by Graham Blatz
       //2/20/15
       //Matches Nearest Neighbor of ostensibly dates, but whatever, man
       {dt1 = Current Data Table(),
              dt2 = Current Data Table(), rt1 = "ReadTime", rt2 = "ReadTime"   
       },
       {DEFAULT LOCAL},
       If( dt1 == dt2,   //default execution of a prompt, dunno a clean way around this without making some user camp inconvenienced.
             
              list_tables = {};
              For( i = 1, i <= N Table(), i++,
                     Insert Into( list_tables, Data Table( i ) << get name );
              );  //sets up names of data tables to pick from
              dt1 = Data Table( list_tables[1] );
              dt2 = Data Table( list_tables[2] ); //Reasonable Starting point
              win_tableselect = New Window( "Select Tables",
                     <<modal,
                     lup_box = Lineup Box( N Col( 2 ),
                           Text Box( "Main Table (i.e. SubID List)", <<set font size( 10 ) ),
                           Text Box( "Update-With Table (i.e. Tool Data)", <<set font size( 10 ) ),
                           c1 = Combo Box(
                                  list_tables
                                  ,
                                  s1 = c1 << getselected;
                                  dt1 = Data Table( s1 );
                                  vref1[1] << delete;
                                  vref1 << append( //Deletes and redraws
                                         col1 = Col List Box(
                                                Data Table( dt1 ),
                                                all,
                                                maxSelected( 1 ),
                                                rt1 = col1 << get selected
                                         )
                                  )
                                  ;
                           ),  //these parts change the following collistboxes and their fantastic syntax based upon the data table combo box selection
                           c2 = Combo Box(
                                  list_tables,
                                  <<set( 2 ),
                                  s2 = c2 << getselected;
                                  dt2 = Data Table( s2 );
                                  vref2[1] << delete;
                                  vref2 << append(
                                         col2 = Col List Box(
                                                Data Table( dt2 ),
                                                all,
                                                maxSelected( 1 ),
                                                rt2 = col2 << get selected
                                         )
                                  );
                           ),  //see above
                           vref1 = V List Box(
                                  col1 = Col List Box(
                                         Data Table( dt1 ),//still stoopdi
                                         all,
                                         maxSelected( 1 ),
                                         rt1 = col1 << get selected
                                  )
                           ),
                           vref2 = V List Box(
                                  col2 = Col List Box(
                                         Data Table( dt2 ),
                                         all,
                                         maxSelected( 1 ),
                                         rt2 = col2 << get selected
                                  )
                           ),    //the point of the vrefs is to have something to delete without messing with the lineupbox's proclivity for left-right ordering
                     ),
                     Text Box( "" ),
                     Hlistbox(
                                  Button Box( "Ok",
                                  s1 = c1 << getselected;
                                  s2 = c2 << getselected;
                                  dt1 = Data Table( s1 );
                                  dt2 = Data Table( s2 );
                                  rt1 = (col1 << get selected)[1];
                                  rt2 = (col2 << get selected)[1];
                           ),  //not everyone has the << return result option yet if they're not in 11
                           Button Box( "Cancel"),
                     ),
              );
              If(win_tableselect["Button"] == -1, Throw("Canceled by User"))
       );
      
       dt2 << New Column( "Loc Min", numeric, continuous, formula( Row() ) ); //indexing, basically, to match/update upon
       list_big = Column( dt2, rt2 ) << get values; //creates a vector of values for the following
      
       dt1 << New Column( "Loc Min",
              numeric,
              continuous,
              formula(
                     Loc Min( Abs( Column( rt1 )[Row()] - list_big ) )
              )
       );   //simple, but fast.  Very fast.
      
       dt_join = dt1 << Join(
              With( dt2 ),
              By Matching Columns( :Loc Min = :Loc Min ),
              Drop multiples( 0, 1 ),
              Name( "Include non-matches" )(0, 0),
              Preserve main table order( 1 )
       );  // I opted for a join rather than an update, for now. may change in the future.
      
       dt1 << delete columns("Loc Min");
       dt2 << delete columns("Loc Min");  //Removes pesky join column
       list_columns = dt_join << get column names( string );
      
       For( i = 1, i <= N Items( list_columns ), i++,
              If( Contains( list_columns, "Loc Min" ),
                     Column( dt_join, list_columns ) << set selected( 1 )
              );
       );  //the joined table will having lingering Loc Min-like columns.  This just hunts them down and removes them.
       dt_join << delete columns;
       dt_join << set name( Char( dt1 << get name ) || " & " || Char( dt2 << get name ) );  //Good enough
       return(dt_join);
);