From 29c8fa05f3acafd41cb655de9ffef3e5929d2f74 Mon Sep 17 00:00:00 2001 From: irvinlesh Date: Thu, 4 Feb 2021 18:51:12 -0800 Subject: [PATCH 1/2] Solved problems 1 and 2 for Backend Test --- ontraport.py | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 ontraport.py diff --git a/ontraport.py b/ontraport.py new file mode 100644 index 0000000..6744a02 --- /dev/null +++ b/ontraport.py @@ -0,0 +1,142 @@ +""" +ontraport.py +By: Irvin Leshchinsky | 2/4/2021 +Compatibility: Python 3.8.5 +(In order to ensure correct function please use v 3.8.5 or higher) + +Collection of functions set to solve Problems 1 and 2 as defined +in https://github.com/Ontraport/Backend-Test. + +Usage: +- multi_to_single(multi_container) solves Problem 1 +- single_to_multi(single_container) solves Problem 2 + +""" + +from collections import deque + +def deep_merge(dict1: dict, dict2: dict) -> dict: + """ + Function takes in two dictionaries and merges them into into first dict, + combining sub-dictionaries and sub-lists into the same key instead of overwriting them + like update() does. In cases where the value is not a dict or list, function acts like update() + + Args: + dict1 (dict): A primary dictionary you want to merge another dictionary in to + dict2 (dict): A secondary dictionary that you want to merge into the previous + Returns: + A merged dictionary that contains the merged values from dict1 and dict2 + + """ + q = deque([(dict1, dict2)]) + while len(q) > 0: + d1, d2 = q.pop() + for k, v in d2.items(): + if k in d1 and isinstance(d1[k], dict) and isinstance(v, dict): + q.append((d1[k], v)) + elif k in d1 and isinstance(d1[k], list) and isinstance(v, list): + d1[k] = d1[k] + v + else: + d1[k] = v + return dict1 + +def multi_to_single(multi_container: dict) -> dict: + """ + Function takes in a dictionary of any dimension or size and compresses it, + creating a one-dimensional dictionary whose keys are string representing a + path that indicates the original structure of the dictionary + + Args: + multi_container (dict): A dictionary of any dimension or size with string keys + Returns: + A single-dimensional dictionary + + """ + return multi_to_single_rec(multi_container, "") + +def multi_to_single_rec(inner_container: dict, built_key: str) -> dict: + """ + Recursive helper function of multi_to_single(). Recurses down a dictionary branch + and builds a singular key/value dictionary that is eventually merged to {final}. + + Args: + inner_container (dict): A dictionary of any dimension or size with string keys + built_key (str): A string consisting of the path traversed down a dictionary branch + Returns: + A single-dimensional dictionary + + """ + final = {} + for item in inner_container.items(): + key, value = item + if (isinstance(value, dict)): + final.update(multi_to_single_rec(value, built_key + str(key) + '/')) + elif (isinstance(value, list)): + built_key = built_key + str(key) + '/' + for list_index, list_val in zip(list(range(0,len(value))),value): + final[built_key + str(list_index)] = list_val + else: + final[built_key + str(key)] = value + return final + +def single_to_multi(single_container: dict) -> dict: + """ + Function takes in a single-dimensional dictionary and expands it to a multi-dimensional dictionary, + whose structure is based on the path represented in each key. For condensed [list] values, the function + assumes the order of the key/pairs to reflect their relative place in the [list], + matching the condensing order of multi_to_single(). + + Args: + single_container (dict): A single-dimensional dictionary + Returns: + An multi-dimensional dictionary that is the expanded form of the initial dictionary + + """ + final = {} + for key, value in single_container.items(): + current = {} + split_keys = str(key).split('/') + temp_list = None + for cur_key in split_keys[::-1]: # We are going backwards to get the base key/val pair + if len(current) == 0: + if (cur_key.isnumeric()): # If we reach a list item, convert the value to a [list] + value = [value] + else: + current[cur_key] = value + else: + temp = current + current = {} + current[cur_key] = temp + deep_merge(final,current) + return final + +def main(): + multi_container = { + 'one': + { + 'two': 3, + 'four': [5,6,7] + }, + 'eight': + { + 'nine': + { + 'ten':11 + } + } + } + + single_container = { + 'one/two':3, + 'one/four/0':5, + 'one/four/1':6, + 'one/four/2':7, + 'eight/nine/ten':11 + } + + # Simple test based off of prompt + print(multi_to_single(multi_container)) + print(single_to_multi(single_container)) + +if __name__ == "__main__": + main() From d4f7f76f9e45b119e26454978c16df1ee4e1ed48 Mon Sep 17 00:00:00 2001 From: irvinlesh Date: Thu, 4 Feb 2021 18:55:56 -0800 Subject: [PATCH 2/2] Minor formatting fixes --- ontraport.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/ontraport.py b/ontraport.py index 6744a02..85e49f6 100644 --- a/ontraport.py +++ b/ontraport.py @@ -2,7 +2,7 @@ ontraport.py By: Irvin Leshchinsky | 2/4/2021 Compatibility: Python 3.8.5 -(In order to ensure correct function please use v 3.8.5 or higher) +(In order to ensure correct functionality please use v 3.8.5 or higher) Collection of functions set to solve Problems 1 and 2 as defined in https://github.com/Ontraport/Backend-Test. @@ -17,13 +17,14 @@ def deep_merge(dict1: dict, dict2: dict) -> dict: """ - Function takes in two dictionaries and merges them into into first dict, + Function takes in two dictionaries and merges them into first dict, combining sub-dictionaries and sub-lists into the same key instead of overwriting them like update() does. In cases where the value is not a dict or list, function acts like update() Args: dict1 (dict): A primary dictionary you want to merge another dictionary in to dict2 (dict): A secondary dictionary that you want to merge into the previous + Returns: A merged dictionary that contains the merged values from dict1 and dict2 @@ -43,11 +44,12 @@ def deep_merge(dict1: dict, dict2: dict) -> dict: def multi_to_single(multi_container: dict) -> dict: """ Function takes in a dictionary of any dimension or size and compresses it, - creating a one-dimensional dictionary whose keys are string representing a + creating a one-dimensional dictionary whose keys are strings representing a path that indicates the original structure of the dictionary Args: multi_container (dict): A dictionary of any dimension or size with string keys + Returns: A single-dimensional dictionary @@ -62,6 +64,7 @@ def multi_to_single_rec(inner_container: dict, built_key: str) -> dict: Args: inner_container (dict): A dictionary of any dimension or size with string keys built_key (str): A string consisting of the path traversed down a dictionary branch + Returns: A single-dimensional dictionary @@ -88,6 +91,7 @@ def single_to_multi(single_container: dict) -> dict: Args: single_container (dict): A single-dimensional dictionary + Returns: An multi-dimensional dictionary that is the expanded form of the initial dictionary