how to generate the shortest string from two string - python-3.x

I would like to write a function to return the shortest string C from two string, A, B and make sure string A, B is substring of C. But the key is length of A does not have to longer than B
ex:
A: 'abcd', B: 'cde' = > C: 'abcde' # c,d is duplicated
A: 'abcd', B: 'ecd' = > C: 'abcdecd' #no character duplicated so C is A + B
A: 'abc', B: 'cdeab' = > C: 'cdeabc'
A: 'bce', B: 'eabc' = > C: 'eabce' #length of eabcd is 5, length of bceabc is 6
A: '', B: 'abc' = > C: 'abc'
A: 'abc', B: '' = > C: 'abc'
I have following function but it seems like it is not correct
def checksubstring(A, B):
if not A or not B: return A if not B else B
index, string = 0, ''
for i, c in enumerate(A):
index = index + 1 if c == B[index] else 0
string += c
return string + B[index:]

You can back up from the end looking for a match like:
Code:
def shortest_substring(a, b):
def checksubstring(a, b):
if not a or not b:
return b or a
for i in range(1, len(b)):
if a[len(a) - i:] == b[:i]:
return a + b[i:]
return a + b
x = checksubstring(a, b)
y = checksubstring(b, a)
return x if len(x) <= len(y) else y
Test Code:
results = [
{'A': 'abcd', 'B': 'cde', 'C': 'abcde'},
{'A': 'abcd', 'B': 'ecd', 'C': 'abcdecd'},
{'A': 'abc', 'B': 'cdeab', 'C': 'cdeabc'},
{'A': 'bce', 'B': 'eabc', 'C': 'eabce'},
{'A': '', 'B': 'abc', 'C': 'abc'},
{'A': 'abc', 'B': '', 'C': 'abc'},
]
for result in results:
assert result['C'] == shortest_substring(result['A'], result['B'])

You must check A,B and B,A, and after this check their results:
def checksubstring(A, B):
if not A or not B: return A if not B else B
index, string = 0, ''
for i, c in enumerate(A):
index = index + 1 if c == B[index] else 0
string += c
return string + B[index:]
def test(A, B):
s1 = checksubstring(A, B)
s2 = checksubstring(B, A)
if len(s1) > len(s2):
return s2
else:
return s1
print(test('abcd', 'cde')) # = > C: 'abcde' # c,d is duplicated
print(test('abcd', 'ecd')) # = > C: 'abcdecd' #no character duplicated so C is A + B
print(test('abc', 'cdeab')) # = > C: 'cdeabc'
print(test('bce', 'eabc')) # = > C: 'eabce' #length of eabcd is 5, length of bceabc is 6
print(test('', 'abc')) # = > C: 'abc'
print(test('abc', '')) # = > C: 'abc'

Related

How to check the values in two dictionaries have the same type?

For example, I have two dictionaries having the same keys:
a = {"a": 1, "b": 2, "c":4.5, "d":[1,2], "e":"string", "f":{"f1":0.0, "f2":1.5}}
b = {"a": 10, "b": 20, "c":3.5, "d":[0,2,4], "e":"q", "f":{"f1":1.0, "f2":0.0}}
and I want to compare the types. My code is something like this:
if type(a["a"]) == type(b["a"]) and type(a["b"]) == type(b["b"]) and type(a["c"]) == type(b["c"]) and type(a["d"]) == type(b["d"]) and type(a["e"]) == type(b["e"]) and type(a["f"]) == type(b["f"]) and type(a["f"]["f1"]) == type(b["f"]["f1"]) and type(a["f"]["f2"]) == type(b["f"]["f2"]):
first_type = type(b["d"][0])
if all( (type(x) is first_type) for x in a["d"] )
#do something
pass
Is there a better way to do it?
You can make a list of the common keys between the dicts:
common_keys = a.keys() & b.keys()
and then iterate over them to check the types:
for k in common_keys:
if type(a[k]) == type(b[k]):
print("Yes, same type! " + k, a[k], b[k])
else:
print("Nope! " + k, a[k], b[k])
and if you wanted to go deeper, check if any of the items are dicts, rinse an repeat
for k in common_keys:
if type(a[k]) == type(b[k]):
print("Yes, same type! " + k, type(a[k]), type(b[k]))
if isinstance(a[k], dict):
ck = a[k].keys() & b[k].keys()
for key in ck:
if type(a[k][key]) == type(b[k][key]):
print("Yes, same type! " + key, type(a[k][key]), type(b[k][key]))
else:
print("Nope!")
else:
print("Nope! " + k, type(a[k]), type(b[k]))
You can use a for loop to iterate through the dicts:
same_types = True
for key in a.keys():
if type(a[key]) != type(b[key]):
same_types = False
break
# if the value is a dict, check nested value types
if type(a[key]) == dict:
for nest_key in a[key].keys():
if type(a[key][nest_key]) != type(b[key][nest_key]):
same_types = False
break
# if the value is a list, check all list elements
# I just simply concat two lists together, you can also refer to
# https://stackoverflow.com/q/35554208/19322223
elif type(a[key]) == list:
first_type = a[key][0]
for elem in a[key] + b[key]:
if type(elem) != first_type:
same_types = False
break
if not same_types:
break
if same_types:
# do something
With the following helper function:
def get_types(obj, items=None):
"""Function that recursively traverses 'obj' and returns
a list of all values and nested values types
"""
if not items:
items = []
if isinstance(obj, dict):
for value in obj.values():
if not isinstance(value, (dict, list, set, tuple)):
items.append(value)
else:
get_types(value, items)
elif isinstance(obj, (list, set, tuple)):
for value in obj:
get_types(value, items)
else:
items.append(obj)
return [type(x) for x in items]
You can compare two dictionaries' values types however deeply nested these are, like this:
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
Since, in your example, a misses one value for d key ([1, 2]) compared to the other dict ([0, 2, 4]), nothing will be printed.
Let's take another example where both dictionaries have the same shape this time, but one value of different type (f2):
a = {"a": 1, "b": [[1, 2], [3, [4]]], "c": {"c1": 0.0, "c2": {"x": "9"}}}
b = {"d": 7, "e": [[2, 1], [5, [7]]], "f": {"f1": 8.9, "f2": {"y": 9}}}
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
Then again, nothing will be printed.
But if you replace 9 by "9" in b["f2"]:
a = {"a": 1, "b": [[1, 2], [3, [4]]], "c": {"c1": 0.0, "c2": {"x": "9"}}}
b = {"d": 7, "e": [[2, 1], [5, [7]]], "f": {"f1": 8.9, "f2": {"y": "9"}}}
if get_types(a) == get_types(b):
print("Each a and b values are of same types")
# Output
# Each a and b values are of same types

Creating new dictionaries using keys from main dict[BEGINNER]

I'm trying to check whether a specific key ends with ":F" or ":M", so i can create new dictionaries called male and female.
d = {'Jane:F': 3, 'Tom:M': 2, 'Jeff:M': 5, 'Mary:F': 3} #initial dict
male = {'Tom': 2, 'Jeff': 5} #required output
female = {'Jane': 3, 'Mary': 3} #required output
You can split the key by : and then check for M and/or F:
d = {'Jane:F': 3, 'Tom:M': 2, 'Jeff:M': 5, 'Mary:F': 3}
male, female = {}, {}
for k, v in d.items():
k, gender = k.split(':')
if gender == 'M':
male[k] = v
else:
female[k] = v
print(male)
print(female)
Prints:
{'Tom': 2, 'Jeff': 5}
{'Jane': 3, 'Mary': 3}
Another version using dict-comprehensions:
male = {k.split(':')[0]: v for k, v in d.items() if k.endswith(':M')}
female = {k.split(':')[0]: v for k, v in d.items() if k.endswith(':F')}

count the frequency of each character using the dictionary in python

My program that takes a string as an input from the user and counts the frequency of each character using the dictionary.
Input:
Python programming is fun
Expected output:
{'p': 2, 'y': 1, 't': 1, 'h': 1, 'o': 2, 'n': 3, 'r': 2, 'g': 2, 'a': 1, 'm': 2, 'i': 2, 's': 1, 'f': 1, 'u': 1}
My code:
string = input().lower()
dicx = {}
count = 0
for i in string:
dicx['i'] = ''
print(dicx)
Use collections.Counter
dicx = collections.Counter(string.lower())
You can iterate over string and update the dictionary accordingly and also there's no need of any count variable.
test_str = input().lower()
dicx = {}
for i in test_str:
if i in dicx:
dicx[i] += 1
else:
dicx[i] = 1
print(dicx)
Function takes input as string and counts the character and stores them in a dictionary
from typing import Dict
char_dict = {} #type: Dict[str, int]
def char_count(string: str) -> dict:
new_string = string.lower()
for c in new_string:
if c in char_dict:
char_dict[c] += 1
else:
char_dict[c] = 1
return char_dict
if __name__ == "__main__":
UserString = input("Enter Input String: ")
CharCount = char_count(UserString)
print("Characters Count: ", CharCount)
Example:
Enter Input String: Python programming is fun
Characters Count: {'p': 2, 'y': 1, 't': 1, 'h': 1, 'o': 2, 'n': 3, ' ': 3, 'r': 2, 'g': 2, 'a': 1, 'm': 2, 'i': 2, 's': 1, 'f': 1, 'u': 1}
Way 1: For
symbols = {}
for s in inp_str.lower():
if s in symbols:
symbols[s] += 1
else:
symbols.update({s: 1})
print(symbols)
Way 2: defaultdict
symbols = defaultdict(int)
for s in inp_str.lower():
symbols[s] += 1
print(symbols)
Way 3: Counter
symbols = Counter(inp_str.lower())
print(symbols)
def charCounter(string):
empty = {}
for i in string.lower():
if i in empty.keys():
empty[i] += 1
else:
empty[i] = 1
return empty
print(charCounter("Oh, it is python"))
d = {}
test_str = input().lower()
for x in test_str:
d[x] = d.get(x,0) + 1
print(d)
much more elegant like this

get list from index of other list python

I have 2 lists:
a=[0,2,0,5]
b=[3,4,5,6]
I want to find remove all the 0 from list a and remove corresponding values(with same index) in list b.
My result should be:
a=[2,5]
b=[4,6]
until now I did:
a = [idx for idx, val in enumerate(a) if val == 0]
and get a=[1,3]
but I don't manage to get the corresponding list in b
a=[0,2,0,5]
b=[3,4,5,6]
a, b = map(list, zip(*[[i, j] for i, j in zip(a, b) if i != 0]))
print(a)
print(b)
Prints:
[2, 5]
[4, 6]
You got a list indexes correctly, to get valid elements from b list the easy way is to do
[b[idx] for idx, val in enumerate(a) if val != 0]
and to get a values
[val for val in a if val != 0]
to do it in one iteration:
x = [(val, b[idx]) for idx, val in enumerate(a) if val != 0]
or
x = [(val_a, val_b) for val_a, val_b in zip(a, b) if val_a != 0]
but it gives you list of tuples, but you can use some python magic to turn it into two lists
a, b = map(list, zip(*x))

Finding commonly occurring words across multiple lists

I have 5 lists of words. I need to find all words occurring in more than 2 lists. Any word can occur multiple times in a list.
I have used collections.Counter but it only returns the frequencies of all the words in individual lists.
a = ['wood', 'tree', 'bark', 'log']
b = ['branch', 'mill', 'boat', 'boat', 'house']
c = ['log', 'tree', 'water', 'boat']
d = ['water', 'log', 'branch', 'water']
e = ['branch', 'rock', 'log']
For example, the output from these lists should be ['log':4, 'branch':3] as 'log' is present in 4 lists and 'branch' in 3.
Without Counter:
a = ['wood', 'tree', 'bark', 'log']
b = ['branch', 'mill', 'boat', 'boat', 'house']
c = ['log', 'tree', 'water', 'boat']
d = ['water', 'log', 'branch', 'water']
e = ['branch', 'rock', 'log']
all_lists = [a, b, c, d, e]
all_words = set().union(w for l in all_lists for w in l)
out = {}
for word in all_words:
s = sum(word in l for l in all_lists)
if s > 2:
out[word] = s
print(out)
Prints:
{'branch': 3, 'log': 4}
Edit (to print the names of lists):
a = ['wood', 'tree', 'bark', 'log']
b = ['branch', 'mill', 'boat', 'boat', 'house']
c = ['log', 'tree', 'water', 'boat']
d = ['water', 'log', 'branch', 'water']
e = ['branch', 'rock', 'log']
all_lists = {'a':a, 'b':b, 'c':c, 'd':d, 'e':e}
all_words = set().union(w for l in all_lists.values() for w in l)
out = {}
for word in all_words:
s = sum(word in l for l in all_lists.values())
if s > 2:
out[word] = s
for k, v in out.items():
print('Word : {}'.format(k))
print('Count: {}'.format(v))
print('Lists: {}'.format(', '.join(kk for kk, vv in all_lists.items() if k in vv )))
print()
Prints:
Word : log
Count: 4
Lists: a, c, d, e
Word : branch
Count: 3
Lists: b, d, e
you can sum the counters - starting with an empty Counter():
from collections import Counter
lists = [a, b, c, d, e]
total = sum((Counter(set(lst)) for lst in lists), Counter())
# Counter({'log': 4, 'branch': 3, 'tree': 2, 'boat': 2, 'water': 2,
# 'wood': 1, 'bark': 1, 'house': 1, 'mill': 1, 'rock': 1})
res = {word: occ for word, occ in total.items() if occ > 2}
# {'log': 4, 'branch': 3}
note that i convert all the lists to a set first in order to avoid double-counts for the words that are more than once in the same list.
if you need to know what list the words were from you could try this:
lists = {"a": a, "b": b, "c": c, "d": d, "e": e}
total = sum((Counter(set(lst)) for lst in lists.values()), Counter())
# Counter({'log': 4, 'branch': 3, 'tree': 2, 'boat': 2, 'water': 2,
# 'wood': 1, 'bark': 1, 'house': 1, 'mill': 1, 'rock': 1})
res = {word: occ for word, occ in total.items() if occ > 2}
# {'log': 4, 'branch': 3}
word_appears_in = {
word: [key for key, value in lists.items() if word in value] for word in res
}
# {'log': ['a', 'c', 'd', 'e'], 'branch': ['b', 'd', 'e']}

Resources