def join(self, right_table, on=None, right_prefix='R.', outer=False):
        """
        Inner-joins another DataTable to this one using `on` (iterable of join
        keys). If two tables share columns other than the join keys, appends
        right_prefix to the right table's column name. If `on` is not
        provided, performs a 'natural join' using all columns of the same name.
        """
        if on is None:  # if no 'on', perform natural join
            on = list(set(self.fields).intersection(set(right_table.fields)))
        if isinstance(on, basestring):
            on = [on]
            
        def get_join_key(row):
            return tuple(row[header] for header in on)
        
        keymap = defaultdict(list)
        for right_row in right_table:
            keymap[get_join_key(right_row)].append(right_row)
        new_table = []
        for left_row in self:
            left_key = get_join_key(left_row)
            if left_key in keymap:
                left_dict = dict(left_row.items())
                for right_row in keymap[left_key]:
                    left_dict_copy = left_dict.copy()
                    for field, val in right_row.items():
                        if field in on:
                            continue
                        elif field in left_row:
                            left_dict_copy[right_prefix + field] = val
                        else:
                            left_dict_copy[field] = val
                    new_table.append(left_dict_copy)
            elif outer:
                    left_dict_copy = dict(left_row.items()).copy()
                    for field in right_table.fields:
                        if field in on:
                            continue
                        elif field in left_row:
                            left_dict_copy[right_prefix + field] = None
                        else:
                            left_dict_copy[field] = None
                    new_table.append(left_dict_copy)

        return DataTable(new_table)