"""Handy general use functions.""" from collections import Counter from distutils.util import strtobool from itertools import zip_longest import json import os def zip_dict(keys, values): """Return a dict of two lists.""" return dict(zip(keys, values)) def split_zip_dict(s1, s2): """Return a dict of two comma-separated strings.""" keys = s1.split(',') values = s2.split(',') return zip_dict(keys, values) def var_to_bool(s): """Convert various variable types to bool.""" if type(s) is bool: return s if (type(s) is not str and type(s) is not int) or s is None: raise ValueError('Cannot covert {} to a bool'.format(s)) else: if (type(s) is int and int(s) == 0) or not strtobool(s): return_var = False elif (type(s) is int and int(s) == 1) or strtobool(s): return_var = True else: raise ValueError('Cannot covert {} to a bool'.format(s)) return return_var def primitive_dict(obj): """Create a dict from an iterable.""" return { f: k for f, k in vars(obj).items() if ((type(k) is str or type(k) is int or type(k) is bool or type( k) is list or type(k) is dict or type(k) is set or type( k) is float or type(k) is None) and '__' not in f[:2])} def grouper(iterable, n, fillvalue=None): """Collect data into fixed-length list_chunks or blocks.""" # grouper('ABCDEFG', 3, 'x') --> ABC DEF Gxx" args = [iter(iterable)] * n return zip_longest(*args, fillvalue=fillvalue) def list_chunks(l, n): """Yield successive n-sized list_chunks from l.""" l2 = list(l) for i in range(0, len(l2), n): yield l2[i:i + n] def dedupe_list(seq): # OrderedDict is faster as of python 3.5 """Dedupe a list while preserving order.""" seen = set() seen_add = seen.add return [x for x in seq if not (x in seen or seen_add(x))] # TODO: add args, etc. to docstring def sanitize_json_dumps(s): """Perform an aggressive clean of a string json data.""" try: clean_json = json.dumps(s, ensure_ascii=False) clean_json = repr(clean_json).replace('\\', '\\')[1:-1] except Exception as e: return json.dumps({'error': str(e)}, ensure_ascii=False) return clean_json # TODO: add args, etc. to docstring def merge_two_dicts(x, y): """Merge two dicts. 2nd overwrites first when keys match.""" z = x.copy() # start with x's keys and values z.update(y) # modifies z with y's keys and values & returns None return z # TODO: add args, etc. to docstring def represents_int(s): """Check if a variable of unknown type can represent an int.""" try: int(s) return True except ValueError: return False def escape_bad_chars(s): """Escape characters which cause problems in str ingestion.""" s = s.replace('\'', '\\\'') return s def which(program): """Determines if an application is installed on the system.""" def is_exe(file_path): return os.path.isfile(file_path) and os.access(file_path, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None def check_for_dupes(elements): """Check if given list contains any duplicates """ setOfElems = set() for elem in elements: if elem in setOfElems: return True else: setOfElems.add(elem) return False # Slower Version of above # def check_for_dupes(list): # """Find and report on duplicates.""" # has_dupes = False # deduped_list = dedupe_list(list) # # if len(list) != len(deduped_list): # has_dupes = True # # return has_dupes def get_duplicates_in_list(list): """Report the duplicates in a list of items""" # Using Counter in case of complex hashable objects. dupe_cnt = Counter() for x in list: dupe_cnt[x] += 1 dupe_dict = dict() for dupe, cnt in dupe_cnt.items(): if cnt > 1: dupe_dict[dupe] = cnt return dupe_dict def compare_lists(list_1, list_2, case_sensitive=False): """Generates a dict report of the difference and intersection of two lists. Args: list_1: (list) a list of elements. list_2: (list) a second list of elements. case_sensitive: (bool) Determines if comparisons will be case sensitive. Returns: dict: A dictionary of comparisons, containing 3 lists, stored in the keys 'list_1_only', 'list_2_only', and 'both' """ comparisons = dict() comparisons['list_1_only'] = list() comparisons['list_2_only'] = list() comparisons['both'] = list() list_1_orig = list_1.copy() list_2_orig = list_2.copy() if not case_sensitive: list_1 = [x.lower() for x in list_1] list_2 = [x.lower() for x in list_2] for pos, el in enumerate(list_1): if el in list_2: comparisons['both'].append(list_1_orig[pos]) else: comparisons['list_1_only'].append(list_1_orig[pos]) comparisons_both_orig = comparisons['both'].copy() if not case_sensitive: comparisons['both'] = [x.lower() for x in comparisons['both']] for pos, el in enumerate(list_2): if el not in comparisons['both']: comparisons['list_2_only'].append(list_2_orig[pos]) comparisons['both'] = comparisons_both_orig return comparisons def check_consecutive(elements): elements = [int(x) for x in elements] return sorted(elements) == list(range(min(elements), max(elements) + 1))