# Data Structures ## Type > Figure out what type of object something is ```python type(object) Example: from collections import Counter nums = [1,2,2,3,3,3] freq=Counter(nums) print(type(freq)) ``` ## Dictionary > Dictionaries are used to store data values in key:value pairs. > ```python from collections import defaultdict dict = {'a':1,'b':2,'c':3} dict.keys() # returns list of keys of dictionary for k in dict.keys(): # enumerate keys print(k). # print keys for key, value in data.items(): # enumerate dictionary print(f"Key: {key}, Value: {value}") dict[key].append(value) # For a specific key, append a value dict[some_key].append([1, 2, 3]) dict.values() # returns list of values of dictionary dict.get('a') # returns value for any corresponding key dict.items() # returns [('a',1),('b',2),('c',3)] dict.copy() # returns copy of the dictionary # NOTE : items() Returns view object that will be updated with any future # changes to dict dict.pop(KEY) # pops key-value pair with that key dict.popitem() # removes most recent pair added dict.setDefault(KEY,DEFAULT_VALUE) # returns value of key, if key exists, else default value returned # If the key exist, this parameter(DEFAULT_VALUE) has no effect. # If the key does not exist, DEFAULT_VALUE becomes the key's value. 2nd # argument's default is None. dict.update({KEY:VALUE}) # inserts pair in dictionary if not present, if present, corresponding value is # overriden (not key) # defaultdict ensures that if any element is accessed that is not present in # the dictionary # it will be created and error will not be thrown (which happens in normal dictionary) # Also, the new element created will be of argument type, for example in the below line # an element of type 'list' will be made for a Key that does not exist myDictionary = defaultdict(list) # anagram example # create a dictionary of lists res = defaultdict(list) # enumerate a list for x, item in enumerate(strs): # sort each word key = ''.join(sorted(item)) # add each word to the dictionary, with sorted word as the key res[key].append(item) return res.values() ``` > Does Key exist? ```python my_dict = {'a': 1} does_a_exist = 'a' in my_dict # True does_b_exist = 'b' in my_dict # False ``` > Function to return keys in dictionary (param hash_map) based on keys from a list (param keys) ```python # input - print(get_values({"Alice": 90, "Bob": 80, "Charlie": 70}, ["Alice", "Bob", "Charlie"])) def get_values(hash_map: Dict[str, int], keys: List[str]) -> List[int]: output = [] for key in keys: output.append(hash_map[key]) return output # output - [90, 80, 70] ``` ## Enumeration Dictionary Key-Value Methods When trying to look at the information in a Python dictionary, there are multiple methods that return objects that contain the dictionary keys and values. `.keys()` returns the keys through a dict_keys object. `.values()` returns the values through a dict_values object. `.items()` returns both the keys and values through a dict_items object. ### Example ```python from collections import Counter nums = [1,2,2,3,3,3] # make dictionary of the count for each number # the key is number, the value is the number of occurance freq=Counter(nums) # print dictionary key value pairs print(freq.items()) for k, v in freq.items(): print("key: " + str(k) + " item: " + str(v)) #outputs # dict_items([(1, 1), (2, 2), (3, 3)]) key: 1 item: 1 key: 2 item: 2 key: 3 item: 3 ``` ## resources https://www.codecademy.com/learn/learn-python-3/modules/learn-python3-dictionaries/cheatsheet ## why would you use one over the other? ```python d = defaultdict(list) d = defualtdict(dict) ``` defaultdict(list) when you need to store multiple items per key and defaultdict(dict) when you need a nested dictionary structure. ### defaultdict(list) Use this when you want each key in the dictionary to map to a list. This is particularly useful when you want to group items together under a single key. Example use case: Storing a list of values for each key, such as grouping words by their first letter. ```python from collections import defaultdict d = defaultdict(list) d['a'].append('apple') d['a'].append('apricot') print(d) # Output: {'a': ['apple', 'apricot']} print(list(d.values()) # returns list type of all keys and values aka dict to list ``` ### defaultdict(dict): Use this when you want each key in the dictionary to map to another dictionary. This is useful for creating nested dictionaries without having to check if the inner dictionary exists. Example use case: Storing complex data structures where each key maps to another dictionary, such as a configuration or settings hierarchy. ```python from collections import defaultdict d = defaultdict(dict) d['config']['setting'] = 'value' print(d) # Output: {'config': {'setting': 'value'}} myDictionary = defaultdict(list) some_key="mykey" nums=[1, 2, 3] myDictionary[some_key].append(nums) print(myDictionary.values()) print(myDictionary.items()) print(myDictionary) # output dict_values([[[1, 2, 3]]]) dict_items([('mykey', [[1, 2, 3]])]) defaultdict(, {'mykey': [[1, 2, 3]]}) ``` ### defaultdict(int) ```python from collections import defaultdict nums = [1, 2, 4, 3, 2, 1] freq = defaultdict(int) for num in nums: freq[num] += 1 print(freq) # defaultdict(, {1: 2, 2: 2, 4: 1, 3: 1}) print (dict(freq)) # {1: 2, 2: 2, 4: 1, 3: 1} ``` ## Counter Python Counter is a container that will hold the count of each of the elements present in the container. The counter is a sub-class available inside the dictionary class. Specifically used for element frequencies from collections import Counter #(capital 'C') # can also be used as 'collections.Counter()' in code ```python list1 = ['x','y','z','x','x','x','y', 'z'] ``` ### Initialization ```python Counter(list1) # => Counter({'x': 4, 'y': 2, 'z': 2}) Counter("Welcome to Guru99 Tutorials!") # => Counter({'o': 3, ' ': 3, 'u': 3, 'e': 2.....}) ``` ### Updating ```python counterObject = collections.Counter(list1) counterObject.keys() = [ 'x' , 'y' , 'z' ] most_common_element = counterObject.most_common(1) # [('x', 4)] counterObject.update("some string") # => Counter({'o': 3, 'u': 3, 'e': 2, 's': 2}) counterObject['s'] += 1 # Increase/Decrease frequency ``` ### Accessing ```python frequency_of_s = counterObject['s'] ``` ### Deleting ```python del couterObject['s'] ``` ### Converting to dictionary 1. Turn a string into a list of characters 2. Count Characters 3. Return dictionary of count of each character ```python input_string="hello" char_list=list(arg_input_string) print(char_list) # ['h', 'e', 'l', 'l', 'o'] counterObject=Counter(char_list) # Create Counter Object print(counterObject) # Counter({'l': 2, 'h': 1, 'e': 1, 'o': 1} print(dict(counterObject)) # print dictionary # {'h': 1, 'e': 1, 'l': 2, 'o': 1} ``` To convert Counter Object to defaultdict object ```-> Dict[str,int]```: ```python input_string="hello" dictionary_list = defaultdict(int) # create default dict object char_list=list(input_string) # create counter object counterObject=Counter(char_list) dict_counter=dict(counterObject) # conver to defaultdict object #fill object for key, value in dict_counter.items(): dictionary_list[key]=value # output defaultdict(, {'h': 1, 'e': 1, 'l': 2, 'o': 1}) ``` # Data Structures *Important data structures for LeetCode* ## Lists > Lists are used to store multiple items in a single variable > ```python nums = [1,2,3] nums.index(1) # returns index nums.append(1) # appends 1 del nums[0] # remove index 0 from list nums.insert(0,10) # inserts 10 at 0th index nums.remove(3) # removes all instances of 3 nums.copy(1) # returns copy of the list numbs.copy() # returns copy of the list nums.deepcopy(original_list) # returns copy of a list that has objects in it nums.count(1) # returns no.of times '1' is present in the list my_list = [0] * 5 # create list of five zeros nums.extend(someOtherList) # ... nums.pop() # pops last element [which element to pop can also be given as optional argument] nums.reverse() # reverses original list (nums in this case) numbers = [1, 2, 3, 4, 5] numbers.reverse() if 3 in some_list # check if item in list or not print(numbers) nums.sort() # sorts list [does NOT return sorted list] for i in range (start,stop): # enumerate over a range in the list for i in range (1,len(some_list)): # enumerate starting at index 1 for the length of "some_list" #Python's default sort uses Tim Sort, which is a combination of both merge sort and insertion sort. ``` > list enumeration ```python numbers = [1, 2, 3, 4, 5] print(numbers) for index, element in enumerate(numbers): print ("index: " + str(index) + " element: " + str(numbers[index])) [1, 2, 3, 4, 5] index: 0 element: 1 index: 1 element: 2 index: 2 element: 3 index: 3 element: 4 index: 4 element: 5 ``` > nested lists > for loop nested list, select element ```python nested_list=[[1, 2, 3], [4, 5, 6], [1, 4]] for sublist in nested_list: first_element=sublist[0] ``` > return each nested list ```python nested_list=[[1, 2, 3], [4, 5, 6], [1, 4]] for i, sublist in enumerate(nested_list): print(f"Sublist {i}: {sublist}")   Sublist 0: [1, 2, 3] Sublist 1: [4, 5, 6] Sublist 2: [1, 4] ``` > access elements of sublist ```python nested_list=[[1, 2, 3], [4, 5, 6], [1, 4]] for i, sublist in enumerate(nested_list): for j, list_item in enumerate(sublist): print("list_index: " + str(j) +" list_item:" + str(list_item)) list_index: 0 list_item:1 list_index: 1 list_item:2 list_index: 2 list_item:3 list_index: 0 list_item:4 list_index: 1 list_item:5 list_index: 2 list_item:6 list_index: 0 list_item:1 list_index: 1 list_item:4 ``` List or String slicing in Python - Resource [Understanding slice notation](https://stackoverflow.com/questions/509211/understanding-slice-notation) ```python +---+---+---+---+---+---+ | P | y | t | h | o | n | +---+---+---+---+---+---+ Slice position: 0 1 2 3 4 5 6 Index position: 0 1 2 3 4 5 >>> p = ['P','y','t','h','o','n'] # Why the two sets of numbers: # indexing gives items, not lists >>> p[0] 'P' >>> p[5] 'n' # Slicing gives lists >>> p[0:1] ['P'] >>> p[0:2] ['P','y'] > Midpoint. Find midpoint of string Example with an Even-Length String (s = "abba"): ```python len(s) = 4 midpoint = len(s) // 2 # = 2 ``` We only need to check indices 0 and 1 (the first two characters) against their corresponding characters from the end. Example with an Odd-Length String (s = "racecar"): ```python len(s) = 7 midpoint = len(s) // 2 = # 3 ``` We only need to check indices 0, 1, and 2 (the first three characters) against their counterparts, ignoring the middle character since it doesn’t affect the palindrome property. > Looping through string comparing first half to second half ```python # Loop through the first half of the string for i in range(midpoint): # Compare the character at the start with the corresponding character at the end if s[i] != s[~i]: # If any characters do not match, it’s not a palindrome return False ``` It's pretty simple really: a[start:stop] # items start through stop-1 a[start:] # items start through the rest of the array a[:stop] # items from the beginning through stop-1 a[:] # a copy of the whole array There is also the step value, which can be used with any of the above: a[start:stop:step] # start through not past stop, by step The key point to remember is that the :stop value represents the first value that is not in the selected slice. So, the difference between stop and start is the number of elements selected (if step is 1, the default). The other feature is that start or stop may be a negative number, which means it counts from the end of the array instead of the beginning. So: a[-1] # last item in the array a[-2:] # last two items in the array a[:-2] # everything except the last two items Similarly, step may be a negative number: a[::-1] # all items in the array, reversed a[1::-1] # the first two items, reversed a[:-3:-1] # the last two items, reversed a[-3::-1] # everything except the last two items, reversed Python is kind to the programmer if there are fewer items than you ask for. For example, if you ask for a[:-2] and a only contains one element, you get an empty list instead of an error. Sometimes you would prefer the error, so you have to be aware that this may happen. Relation to slice() object The slicing operator [] is actually being used in the above code with a slice() object using the : notation (which is only valid within []), i.e.: a[start:stop:step] is equivalent to: a[slice(start, stop, step)] Slice objects also behave slightly differently depending on the number of arguments, similarly to range(), i.e. both slice(stop) and slice(start, stop[, step]) are supported. To skip specifying a given argument, one might use None, so that e.g. a[start:] is equivalent to a[slice(start, None)] or a[::-1] is equivalent to a[slice(None, None, -1)]. While the :-based notation is very helpful for simple slicing, the explicit use of slice() objects simplifies the programmatic generation of slicing. ``` ## Deque > A double-ended queue, or deque, has the feature of adding and removing elements from either end. > ```python #in BFS(Breadth-first search) or other algorithms where we have to pop or add elements to the begining , deque is the best option #we can also use list, but list.pop(0) is O(n) operation where as dequeu.popleft() is O(1) from collections import deque queue = deque(['name','age','DOB']) queue.append("append_from_right") # Append from right queue.pop() # Pop from right queue.appendleft("fromLeft") # Append from left queue.popleft() # Pop from left queue.index(element,begin_index,end_index) # Returns first index of element b/w the 2 indices. queue.insert(index,element) queue.remove() # removes first occurrance queue.count() # obvious queue.reverse() # reverses order of queue elements ``` ## Heapq > As we know the Heap Data Structure is used to implement the Priority Queue ADT. In python we can directly access a Priority Queue implemented using a Heap by using the **Heapq** library/module. > ```python import heapq # (minHeap by Default) nums = [5, 7, 9, 1, 3] heapq.heapify(nums) # converts list into heap. Can be converted back to list by list(nums). heapq.heappush(nums,element) # Push an element into the heap heapq.heappop(nums) # Pop an element from the heap # heappush(heap, ele) :- This function is used to insert the element mentioned # in its arguments into heap. The order is adjusted, so as heap structure is # maintained. # heappop(heap) :- This function is used to remove and return the smallest # element from heap. The order is adjusted, so as heap structure is maintained. # Other Methods Available in the Library # Used to return the k largest elements from the iterable specified # The key is a function with that accepts single element from iterable, # and the returned value from that function is then used to rank that element in the heap heapq.nlargest(k, iterable, key = fun) heapq.nsmallest(k, iterable, key = fun) #Max heap in python #By default heapq in python is min heap, #if we want to use max heap we can simply invert the value of the keys and use heapq. #For example, turn 1000.0 into -1000.0 and 5.0 into -5.0. #The easiest and ideal solution #Multiply the values by -1 #All the highest numbers are now the lowest and vice versa. #Just remember that when you pop an element to multiply it with -1 in order to get the original value again. #Example: import heapq heap = [] heapq.heappush(heap, 1*(-1)) heapq.heappush(heap, 10*(-1)) heapq.heappush(heap, 20*(-1)) print(heap) The output will look like: [-20, -1, -10] #when popping element multiply it with -1 max_element = -heapq.heappop(heap) print(max_element) Output will be: 20 ``` ## Sets > A set is a collection which is unordered, immutable, unindexed, No Duplicates. > ```python myset = set() set = {1,2,3} 3 in set # True set.add(item) set.remove(item) set.discard(item) | set.remove(item) # removes item | remove will throw error if item is not there, discard will not set.pop() # removes random item (since unordered) set.isdisjoint(anotherSet) # returns true if no common elements set.issubset(anotherSet) # returns true if all elements from anotherSet is present in original set set.issuperset(anotherSet) # returns true if all elements from original set is present in anotherSet set.difference(anotherSet) # returns set containing items ONLY in first set set.difference_update(anotherSet) # removes common elements from first set [no new set is created or returned] set.intersection(anotherSet) # returns new set with common elements set.intersection_update(anotherSet) # modifies first set keeping only common elements set.symmetric_difference(anotherSet) # returns set containing all non-common elements of both sets set.symmetric_difference_update(anotherSet) # same as symmetric_difference but changes are made on original set set.union(anotherSet) # ... set.update(anotherSet) # adds anotherSet without duplicate ``` ## Tuples > A [tuple](https://www.scaler.com/topics/python/tuples-in-python/) is a collection which is ordered, unchangeable and can contain duplicate values > - *Operations Time Complexities* Similar to list ```python tuple = (1,2,3,1) tuple.count(1) # returns occurence of an item tuple.index(1) # returns index of 1 in array ``` Tuple Keys ```python dict_of_pairs = {} dict_of_pairs[(0, 0)] = 1 dict_of_pairs[(0, 1)] = 2 print(dict_of_pairs) # {(0, 0): 1, (0, 1): 2} set_of_pairs = set() set_of_pairs.add((0, 0)) set_of_pairs.add((0, 1)) print(set_of_pairs) # {(0, 0), (0, 1)} ``` ## Strings [Python String isnumeric()](https://www.programiz.com/python-programming/methods/string/isnumeric) ```python # ** split Function ** # The split() method breaks up a string at the specified separator and returns # a list of strings. text = 'Python is a fun programming language' # split the text from space print(text.split(' ')) # Output: ['Python', 'is', 'a', 'fun', 'programming', 'language'] # convert string to list s="abcd" s=list(s) print(s) # Output: ['a', 'b', 'c', 'd'] # convert list to string (better if list has non alpha chars) my_list = [1, 2, 3, 'apple'] result = ', '.join(str(item) for item in my_list) print(result) # Output: 1, 2, 3, apple # ** count Function ** # The count() method returns the number of occurrences of a substring in the given string. # Example message = 'python is popular programming language' # number of occurrence of 'p' print('Number of occurrence of p:', message.count('p')) # Output: Number of occurrence of p: 4 # The isnumeric() method returns True if all characters in a string are numeric characters. If not, it returns False. s = '1242323' print(s.isnumeric()) #Output: True # The find() method returns the index of first occurrence of the substring (if found). If not found, it returns -1. # check the index of 'fun' print(message.find('fun')) # Output: 12 # The isalnum() method returns True if all characters in the string are alphanumeric (either alphabets or numbers). If not, it returns False. name = "M3onica Gell22er " print(name.isalnum()) # Output : False # The isalpha() method returns True if all characters in the string are alphabets. If not, it returns False name = "Monica" print(name.isalpha()) #output true # other important functions string.strip([chars]) #The strip() method returns a copy of the string by removing both the leading and the trailing characters (based on the string argument passed). string.upper() # The upper() method converts all lowercase characters in a string into uppercase characters and returns it. string.lower() # The lower() method converts all uppercase characters in a string into lowercase characters and returns it. string.islower() # The islower() method returns True if all cased characters in the string are lowercase and there is at least one cased character, False otherwise. string.isdigit() string.isupper() # The isupper() method returns True if all cased characters in the string are uppercase and there is at least one cased character, False otherwise. ``` > Sorting a string ```python # Sorting a string test_string="bca" res = ''.join(sorted(test_string)) print(res) #abc ``` > String non-numeric characters ```python s="What was that @dawg?" filtered_s=''.join(ch for ch in s if ch.isalnum()) print(filtered_s) ``` # Built-in or Library functions > zip two lists together ```python # call # print(build_hash_map(["Alice", "Bob", "Charlie"], [90, 80, 70])) # function def build_hash_map(keys: List[str], values: List[int]) -> Dict[str, int]: output_dict={} for key, value in zip(keys, values): dict[key]=value return output_dict # return {'Alice': 90, 'Bob': 80, 'Charlie': 70} ``` - Functions to iterate over list / other iterable (tuple, dictionaries) ```python ** map(fun, iter) ** # fun : It is a function to which map passes each element of given iterable. # iter : It is a iterable which is to be mapped. ** zip(list,list) ** for elem1,elem2 in zip(firstList,secondList): # will merge both lists and produce tuples with both elements # Tuples will stop at shortest list (in case of both lists having different len) # Example ''' a = ("John", "Charles", "Mike") b = ("Jenny", "Christy", "Monica") x = zip(a, b) # use the tuple() function to display a readable version of the result: print(tuple(x)) o/p: (('John', 'Jenny'), ('Charles', 'Christy'), ('Mike', 'Monica')) ''' ** any(list) ** [ OPPOSITE IS => ** all() ** ] any(someList) # returns true if ANY element in list is true [any string, all numbers except 0 also count as true] ** enumerate(list|tuple) ** # [when you need to attach indexes to lists or tuples ] enumerate(anyList) # ['a','b','c'] => [(0, 'a'), (1, 'b'), (2, 'c')] ** filter(function|list) ** filter(myFunction,list) # returns list with elements that returned true when passed in function ***************** import bisect *********************** ** bisect.bisect(list,number,begin,end) ** O(log(n)) # [ returns the index where the element should be inserted # such that sorting order is maintained ] a = [1,2,4] bisect.bisect(a,3,0,4) # [1,2,4] => 3 coz '3' should be inserted in 3rd index to maintain sorting order # Other variants of this functions are => bisect.bisect_left() | bisect.bisect_right() # they have same arguments. Suppose the element we want to insert is already present # in the sorting list, the bisect_left() will return index left of the existing number # and the bisect_right() or bisect() will return index right to the existing number # ** bisect.insort(list,number,begin,end) ** O(n) to insert # ** bisect.insort_right(list,number,begin,end) ** # ** bisect.insort_left(list,number,begin,end) ** The above 3 functions are exact same of bisect.bisect(), the only difference is that they return the sorted list after inserting and not the index. The left() right() logic is also same as above. ``` - Getting ASCII value of a character ```python ** ord(str) ** # returns ascii value of the character , Example ord("a") = 97 ** chr(int) ** # return character of given ascii value , Example chr(97) = "a" ``` # Clean Code Tips - **Doc Strings -** Documentation for your functions in the interview to look slick 😎 A docstring is short for documentation string. Python docstrings (documentation strings) are the [string](https://www.programiz.com/python-programming/string) literals that appear right after the definition of a function, method, class, or module. Triple quotes are used while writing docstrings. For example: ``` def double(num): """Function to double the value""" return 2*num ``` Docstrings appear right after the definition of a function, class, or a module. This separates docstrings from multiline comments using triple quotes. The docstrings are associated with the object as their `__doc__` attribute. So, we can access the docstrings of the above function with the following lines of code: ``` def double(num): """Function to double the value""" return 2*num print(double.__doc__) ``` **Output** ``` Function to double the value ``` - Use **Assert keyword** in python for testing edge cases. Looks more professional. ### Definition and Usage The `assert` keyword is used when debugging code. The `assert` keyword lets you test if a condition in your code returns True, if not, the program will raise an AssertionError. You can write a message to be written if the code returns False, check the example below. ```python x = "hello" #if condition returns False, AssertionError is raised: assert x == "goodbye", "x should be 'hello'" ``` - **ALWAYS** be aware of any code snippet that is being **REPEATED** in your solution. **MODULARITY** #1 Priority. Refactoring is also an important part of interview. - This is usually asked as a follow up after coding the solution. *Are there any changes you want to make to this solution?* # Miscellaneous - How to take multiple line input in python? [Taking multiple inputs from user in Python - GeeksforGeeks](https://www.geeksforgeeks.org/taking-multiple-inputs-from-user-in-python/) - Using split() method - Using List comprehension **Syntax :** ``` input().split(separator, maxsplit) ``` ## Example ```python # Python program showing how to # multiple input using split # taking two inputs at a time x, y = input("Enter a two value: ").split() print("Number of boys: ", x) print("Number of girls: ", y) print() # taking three inputs at a time x, y, z = input("Enter a three value: ").split() print("Total number of students: ", x) print("Number of boys is : ", y) print("Number of girls is : ", z) print() # taking two inputs at a time a, b = input("Enter a two value: ").split() print("First number is {} and second number is {}".format(a, b)) print() # taking multiple inputs at a time # and type casting using list() function x = list(map(int, input("Enter a multiple value: ").split())) print("List of students: ", x) ``` ```python # Python program showing # how to take multiple input # using List comprehension # taking two input at a time x, y = [int(x) for x in input("Enter two value: ").split()] print("First Number is: ", x) print("Second Number is: ", y) print() # taking three input at a time x, y, z = [int(x) for x in input("Enter three value: ").split()] print("First Number is: ", x) print("Second Number is: ", y) print("Third Number is: ", z) print() # taking two inputs at a time x, y = [int(x) for x in input("Enter two value: ").split()] print("First number is {} and second number is {}".format(x, y)) print() # taking multiple inputs at a time x = [int(x) for x in input("Enter multiple value: ").split()] print("Number of list is: ", x) # taking multiple inputs at a time separated by comma x = [int(x) for x in input("Enter multiple value: ").split(",")] print("Number of list is: ", x) ``` - Important Python Math Functions [Python Math Module - GeeksforGeeks](https://www.geeksforgeeks.org/python-math-module/) - Log Function [Log functions in Python - GeeksforGeeks](https://www.geeksforgeeks.org/log-functions-python/) ``` Syntax : math.log(a,Base) Parameters :a : The numeric value Base : Base to which the logarithm has to be computed. Return Value : Returns natural log if 1 argument is passed and log with specified base if 2 arguments are passed. Exceptions : Raises ValueError is a negative no. is passed as argument. ``` ```python import math # Printing the log base e of 14 print ("Natural logarithm of 14 is : ", end="") print (math.log(14)) # Printing the log base 5 of 14 print ("Logarithm base 5 of 14 is : ", end="") print (math.log(14,5)) ``` - Finding the ceiling and the floor value - Ceil value means the smallest integral value greater than the number and the floor value means the greatest integral value smaller than the number. This can be easily calculated using the ceil() and floor() method respectively. ```python # Python code to demonstrate the working of # ceil() and floor() # importing "math" for mathematical operations import math a = 2.3 # returning the ceil of 2.3 (i.e 3) print ("The ceil of 2.3 is : ", end="") print (math.ceil(a)) # returning the floor of 2.3 (i.e 2) print ("The floor of 2.3 is : ", end="") print (math.floor(a)) ``` - Other Important functions ```python # Constants # Print the value of Euler e (2.718281828459045) print (math.e) # Print the value of pi (3.141592653589793) print (math.pi) print (math.gcd(b, a)) print (pow(3,4)) # print the square root of 4 print(math.sqrt(4)) a = math.pi/6 b = 30 # returning the converted value from radians to degrees print ("The converted value from radians to degrees is : ", end="") print (math.degrees(a)) # returning the converted value from degrees to radians print ("The converted value from degrees to radians is : ", end="") print (math.radians(b)) ``` ```python ** bin(int) ** bin(anyNumber) # Returns binary version of number ** divmod(int,int) ** divmod(dividend,divisor) # returns tuple like (quotient, remainder) ``` - Python cmp_to_key function to sort list with custom compare function [Sort a list of lists with a custom compare function](https://stackoverflow.com/questions/5213033/sort-a-list-of-lists-with-a-custom-compare-function) ## How the custom comparator works When providing a custom comparator, it should generally return an integer/float value that follows the following pattern (as with most other programming languages and frameworks): - return a negative value (`< 0`) when the left item should be sorted *before* the right item - return a positive value (`> 0`) when the left item should be sorted *after* the right item - return `0` when both the left and the right item have the same weight and should be ordered "equally" without precedence ```python from functools import cmp_to_key sorted(mylist, key=cmp_to_key(compare)) # Example def compare(item1, item2): if fitness(item1) < fitness(item2): return -1 elif fitness(item1) > fitness(item2): return 1 else: return 0 ``` > Python integer division acts a bit weird with -ve numbers ex: -3//2 will give -2 answer instead of -1 so always use int(-3/2) for integer division in problems # Sorting > Sorting with a custom method ```python def get_word_length(word: str) -> int: return len(word) words = ["apple", "banana", "kiwi", "pear", "watermelon", "blueberry", "cherry"] words.sort(key=get_word_length) print(words) # ['kiwi', 'pear', 'apple', 'banana', 'cherry', 'blueberry', 'watermelon'] ``` > Sorting with Lambda ```python words = ["apple", "banana", "kiwi", "pear", "watermelon", "blueberry", "cherry"] words.sort(key=lambda x: len(x)) print(words) # ['kiwi', 'pear', 'apple', 'banana', 'cherry', 'blueberry', 'watermelon'] # sort list in ascending order def sort_words(words: List[str]) -> List[str]: words.sort(key=None, reverse=False) return words # sort based on length in descending order def sort_words(words: List[str]) -> List[str]: words.sort(key=lambda x: len(x), reverse=True) return words print(sort_words(["cherry", "apple", "blueberry", "banana", "watermelon", "zucchini", "kiwi", "pear"])) ``` # Absolute Value with Lambda ```python def sort_numbers(numbers: List[int]) -> List[int]: numbers.sort(key=lambda x: get_abs(x), reverse=False) return numbers print(sort_numbers([1, -5, -3, 2, 4, 11, -19, 9, -2, 5, -6, 7, -4, 2, 6])) ```