def iterateJoinRecords(tablesAndFields)
   
      raise "'iterateJoinRecords' must be called with a block." if not block_given?   
   
      if tablesAndFields and tablesAndFields.is_a?(Array)
         
         # get all the records from QuickBase that we might need - fewer API calls is faster than processing extra data
         tables = []
         numRecords = {}
         tableRecords = {}
         joinfield = {}
         
         tablesAndFields.each{|tableAndFields|
            if tableAndFields and tableAndFields.is_a?(Hash)
               if tableAndFields["dbid"] and tableAndFields["fields"] and tableAndFields["joinfield"]
                  if tableAndFields["fields"].is_a?(Array)
                     tables << tableAndFields["dbid"]
                     joinfield[tableAndFields["dbid"]] = tableAndFields["joinfield"]
                     tableAndFields["fields"] << tableAndFields["joinfield"] if not tableAndFields["fields"].include?(tableAndFields["joinfield"])
                     tableRecords[tableAndFields["dbid"]] = getAllValuesForFields( tableAndFields["dbid"],
                                                                                                          tableAndFields["fields"],
                                                                                                          tableAndFields["query"],
                                                                                                          tableAndFields["qid"],
                                                                                                          tableAndFields["qname"],
                                                                                                          tableAndFields["clist"],
                                                                                                          tableAndFields["slist"],
                                                                                                          "structured",
                                                                                                          tableAndFields["options"])
                     numRecords[tableAndFields["dbid"]] = tableRecords[tableAndFields["dbid"]][tableAndFields["fields"][0]].length                                                                                     
                  else
                     raise "'tableAndFields[\"fields\"]' must be an Array of fields to retrieve."
                  end
               else
                  raise "'tableAndFields' is missing one of 'dbid', 'fields' or 'joinfield'."
               end
            else
               raise "'tableAndFields' must be a Hash"
            end
         }
         
         numTables = tables.length
         
         # go through the records in the first table
         (0..(numRecords[tables[0]]-1)).each{|i|
         
            # get the value of the join field in each record of the first table
            joinfieldValue = tableRecords[tables[0]][joinfield[tables[0]]][i]

            # save the other tables' record indices of records containing the joinfield value 
            tableIndices = []
            
            (1..(numTables-1)).each{|tableNum| 
               tableIndices[tableNum] = []
               (0..(numRecords[tables[tableNum]]-1)).each{|j|
                  if joinfieldValue == tableRecords[tables[tableNum]][joinfield[tables[tableNum]]][j]
                     tableIndices[tableNum] << j
                  end
               }
            }
            
            # if all the tables had at least one matching record, build a joined record and yield it
            buildJoinRecord = true   
            (1..(numTables-1)).each{|tableNum|
               buildJoinRecord = false if not tableIndices[tableNum].length > 0
            }
            
            if buildJoinRecord
            
               joinRecord = {}
               
               tableRecords[tables[0]].each_key{|field|
                  joinRecord[field] = tableRecords[tables[0]][field][i]
               }
               
               # nested loops for however many tables we have
               currentIndex = []
               numTables.times{ currentIndex << 0 }
               loop {
                  (1..(numTables-1)).each{|tableNum|   
                     index = tableIndices[tableNum][currentIndex[tableNum]]
                     tableRecords[tables[tableNum]].each_key{|field|
                        joinRecord[field] = tableRecords[tables[tableNum]][field][index]
                     }
                     if currentIndex[tableNum] == tableIndices[tableNum].length-1
                        currentIndex[tableNum] = -1
                     else
                        currentIndex[tableNum] += 1
                     end   
                     if tableNum == numTables-1
                        yield joinRecord
                     end
                  }
                  finishLooping = true
                  (1..(numTables-1)).each{|tableNum|
                     finishLooping = false if currentIndex[tableNum] != -1
                  }
                  break if finishLooping
               }
            end
         }
      else
         raise "'tablesAndFields' must be Array of Hashes of table query parameters."
      end
   end
def iterateJoinRecords(tablesAndFields)
   
      raise "'iterateJoinRecords' must be called with a block." if not block_given?   
   
      if tablesAndFields and tablesAndFields.is_a?(Array)
         
         # get all the records from QuickBase that we might need - fewer API calls is faster than processing extra data
         tables = []
         numRecords = {}
         tableRecords = {}
         joinfield = {}
         
         tablesAndFields.each{|tableAndFields|
            if tableAndFields and tableAndFields.is_a?(Hash)
               if tableAndFields["dbid"] and tableAndFields["fields"] and tableAndFields["joinfield"]
                  if tableAndFields["fields"].is_a?(Array)
                     tables << tableAndFields["dbid"]
                     joinfield[tableAndFields["dbid"]] = tableAndFields["joinfield"]
                     tableAndFields["fields"] << tableAndFields["joinfield"] if not tableAndFields["fields"].include?(tableAndFields["joinfield"])
                     tableRecords[tableAndFields["dbid"]] = getAllValuesForFields( tableAndFields["dbid"],
                                                                                                          tableAndFields["fields"],
                                                                                                          tableAndFields["query"],
                                                                                                          tableAndFields["qid"],
                                                                                                          tableAndFields["qname"],
                                                                                                          tableAndFields["clist"],
                                                                                                          tableAndFields["slist"],
                                                                                                          "structured",
                                                                                                          tableAndFields["options"])
                     numRecords[tableAndFields["dbid"]] = tableRecords[tableAndFields["dbid"]][tableAndFields["fields"][0]].length                                                                                     
                  else
                     raise "'tableAndFields[\"fields\"]' must be an Array of fields to retrieve."
                  end
               else
                  raise "'tableAndFields' is missing one of 'dbid', 'fields' or 'joinfield'."
               end
            else
               raise "'tableAndFields' must be a Hash"
            end
         }
         
         numTables = tables.length
         
         # go through the records in the first table
         (0..(numRecords[tables[0]]-1)).each{|i|
         
            # get the value of the join field in each record of the first table
            joinfieldValue = tableRecords[tables[0]][joinfield[tables[0]]][i]

            # save the other tables' record indices of records containing the joinfield value 
            tableIndices = []
            
            (1..(numTables-1)).each{|tableNum| 
               tableIndices[tableNum] = []
               (0..(numRecords[tables[tableNum]]-1)).each{|j|
                  if joinfieldValue == tableRecords[tables[tableNum]][joinfield[tables[tableNum]]][j]
                     tableIndices[tableNum] << j
                  end
               }
            }
            
            # if all the tables had at least one matching record, build a joined record and yield it
            buildJoinRecord = true   
            (1..(numTables-1)).each{|tableNum|
               buildJoinRecord = false if not tableIndices[tableNum].length > 0
            }
            
            if buildJoinRecord
            
               joinRecord = {}
               
               tableRecords[tables[0]].each_key{|field|
                  joinRecord[field] = tableRecords[tables[0]][field][i]
               }
               
               # nested loops for however many tables we have
               currentIndex = []
               numTables.times{ currentIndex << 0 }
               loop {
                  (1..(numTables-1)).each{|tableNum|   
                     index = tableIndices[tableNum][currentIndex[tableNum]]
                     tableRecords[tables[tableNum]].each_key{|field|
                        joinRecord[field] = tableRecords[tables[tableNum]][field][index]
                     }
                     if currentIndex[tableNum] == tableIndices[tableNum].length-1
                        currentIndex[tableNum] = -1
                     else
                        currentIndex[tableNum] += 1
                     end   
                     if tableNum == numTables-1
                        yield joinRecord
                     end
                  }
                  finishLooping = true
                  (1..(numTables-1)).each{|tableNum|
                     finishLooping = false if currentIndex[tableNum] != -1
                  }
                  break if finishLooping
               }
            end
         }
      else
         raise "'tablesAndFields' must be Array of Hashes of table query parameters."
      end
   end
Get records from two or more tables and/or queries with the same value in a
 'join' field and loop through the joined results.
 The 'joinfield' does not have to have the same name in each table.
 Fields with the same name in each table will be merged, with the value from the last
 table being assigned. This is similar to an SQL JOIN.