def get_by_cols(self, cols, direction=1):
        """Return the first or last row that satisfies the given col value constraints,
        or None if no row contains the given value.

        Parameters
        ----------
        cols: :obj:'dict'
            Dictionary of col values for a specific row.
        direction: int, optional
            Either 1 or -1. 1 means find the first row, -1 means find the last row.

        Returns
        -------
        :obj:`dict`
            A dictionary mapping keys (header strings) to values, which
            represents a row of the table. This row contains the given value in
            the specified column.
        """
        if direction == 1:
            iterator = range(self.num_rows)
        elif direction == -1:
            iterator = range(self.num_rows-1, -1, -1)
        else:
            raise ValueError("Direction can only be 1 (first) or -1 (last). Got: {0}".format(direction))

        for i in iterator:
            row = self._table[i+1]

            all_sat = True
            for key, val in cols.items():
                if row[key] != val:
                    all_sat = False
                    break

            if all_sat:
                return row.copy()

        return None
Return the first or last row that satisfies the given col value constraints,
        or None if no row contains the given value.

        Parameters
        ----------
        cols: :obj:'dict'
            Dictionary of col values for a specific row.
        direction: int, optional
            Either 1 or -1. 1 means find the first row, -1 means find the last row.

        Returns
        -------
        :obj:`dict`
            A dictionary mapping keys (header strings) to values, which
            represents a row of the table. This row contains the given value in
            the specified column.