def read_csv(filename): """Read a CSV file, return list of rows""" import csv with open(filename, 'rt', newline='') as f: reader = csv.reader(f, skipinitialspace=True) return [row for row in reader] def header_map(headers): """ Read a list, and convert it to a dictionary where the key is each element of the given list and the value is its position in the list :param headers: List :return: Dict """ header_dict = dict() i = 0 for header in headers: header_dict[header] = i i = i + 1 return header_dict def select(table, search_items): """ Read the set in the second argument and search through the table in the first argument and return the columns that match the query in the search items :param table: List :param search_items: List :return: List """ header_numbers = header_map(table[0]) ret_list = list() columns = list() for item in search_items: if type(item) is int: # Convert searched elements into strings if it is a number columns.append(header_numbers[str(item)]) else: columns.append(header_numbers[item]) columns.sort() for item in table: lst = list() for number in columns: lst.append(item[number]) ret_list.append(lst) return ret_list def row2dict(hmap, row): """ Convert a row in the second argument, given the headers in the first argument to a dictionary which uses the headers as keys and the row data as values :param hmap: Dictionary :param row: List :return: Dictionary """ ret_dict = dict() for key in hmap: ret_dict[key] = row[hmap[key]] return ret_dict def check_row(row, query): """ Check the row in the first argument passes a query in the second argument. The second argument is a formatted tuple where the first element in the tuple is a column name, the second is an operation (==, <=, >=, AND, OR) and the third element is a condition, numeric or string matching. (ex: is age == 34, is color == blue). AND and OR are special in that you can pass in recursive tuples (left and right argument are tuples) that will also be evaluated recursively :param row: List :param query: Tuple :return: Boolean """ def perform_operation(op, var, cond): if type(var) is str: if var.isnumeric(): var = int(var) if type(cond) is str: if cond.isnumeric(): cond = int(cond) if op == '==': return var == cond elif op == '>=': return var >= cond elif op == '<=': return var <= cond elif op == 'OR': return perform_operation(var[1], row[var[0]], var[2]) or perform_operation(cond[1], row[cond[0]], cond[2]) elif op == 'AND': return perform_operation(var[1], row[var[0]], var[2]) and perform_operation(cond[1], row[cond[0]], cond[2]) else: return False if type(query[0]) and type(query[2]) is tuple: return perform_operation(query[1], query[0], query[2]) else: stringify = str(query[2]) return perform_operation(query[1], row[str(query[0])], stringify) def filter_table(table, query): """ This function takes a table of csv values, and performs the query to filter out the rows that do not match the query :param table: List :param query: Tuple :return: List """ header_row = header_map(table[0]) data_rows = table[1:] result = list() result.append(table[0]) for row in data_rows: data_dict = row2dict(header_row, row) if check_row(data_dict, query): result.append(row) return result