Search table without repetitive for loops I have an astropy table with a column containing datetime.datetime instances. I want to isolate the rows of this table that correspond to a user specified year, month, day, or hour. The table I am working with has a little over 100,000 entries, but as a more manageable example you can consider the following table with only six entries:

data = [1, 2, 3, 4, 5, 6]
dates = [datetime.datetime(2010, 6, 25, 0, 15),
         datetime.datetime(2011, 5, 25, 0, 45),
         datetime.datetime(2011, 6, 25, 1, 15),
         datetime.datetime(2012, 7, 12, 3, 45),
         datetime.datetime(2013, 9, 15, 2, 15),
         datetime.datetime(2014, 2, 25, 2, 45)]

test_tab = Table(data=[dates, data], names=['date', 'data'])


To search this table I came up with the following function:

import numpy as np
def search(year=None, month=None, day=None, hour=None):

    indx = [True for i in test_tab['date']]
    if year:
        test_func = np.vectorize(lambda x: x.year == year)
        indx = np.logical_and(indx, test_func(test_tab['date']))

    if month:
        test_func = np.vectorize(lambda x: x.month == month)
        indx = np.logical_and(indx, test_func(test_tab['date']))

    if day:
        test_func = np.vectorize(lambda x: x.day == day)
        indx = np.logical_and(indx, test_func(test_tab['date']))

    if hour:
        test_func = np.vectorize(lambda x: x.hour == hour)
        indx = np.logical_and(indx, test_func(test_tab['date']))

    return test_tab[np.where(indx)[0]]


My problem is that this function executes 4 loops over the same table. Is it possible to accomplish the same thing more efficiently?
I am not proficient with NumPy and I am sure there is a better way, but, as a step to following the DRY principle and making the code a bit more scalable, you can dynamically determine and "and" all the filtering conditions:

import datetime

from astropy.table import Table
import numpy as np


def search(test_tab, **params):
    def vectorize_callable(item):
        return all(getattr(item, param_name) == param_value
                   for param_name, param_value in params.items()
                   if param_value is not None)

    return test_tab[np.where(np.vectorize(vectorize_callable)(test_tab['date']))[0]]


data = [1, 2, 3, 4, 5, 6]
dates = [datetime.datetime(2010, 6, 25, 0, 15),
         datetime.datetime(2011, 5, 25, 0, 45),
         datetime.datetime(2011, 6, 25, 1, 15),
         datetime.datetime(2012, 7, 12, 3, 45),
         datetime.datetime(2013, 9, 15, 2, 15),
         datetime.datetime(2014, 2, 25, 2, 45)]


test_tab = Table(data=[dates, data], names=['date', 'data'])
print(search(test_tab, year=2011, day=25))


Prints:

        date        data
------------------- ----
2011-05-25 00:45:00    2
2011-06-25 01:15:00    3


Note how all() in combination with getattr() are used to "join" the logical conditions applied to different attributes.