Using python need to get the substrings - python-3.x

Q)After executing the code Need to print the values [1, 12, 123, 2, 23, 3, 13], but iam getting [1, 12, 123, 2, 23, 3]. I have missing the letter 13. can any one tell me the reason to overcome that error?
def get_all_substrings(string):
length = len(string)
list = []
for i in range(length):
for j in range(i,length):
list.append(string[i:j+1])
return list
values = get_all_substrings('123')
results = list(map(int, values))
print(results)
count = 0
for i in results:
if i > 1 :
if (i % 2) != 0:
count += 1
print(count)

Pretty straight forward issue in your nested for loops within get_all_substrings(), lets walk it!
You are iterating over each element of your string 123:
for i in range(length) # we know length to be 3, so range is 0, 1, 2
You then iterate each subsequent element from the current i:
for j in range(i,length)
Finally you append a string from position i to j+1 using the slice operator:
list.append(string[i:j+1])
But what exactly is happening? Well we can step through further!
The first value of i is 0, so lets skip the first for, go to the second:
for j in range(0, 3): # i.e. the whole string!
# you would eventually execute all of the following
list.append(string[0:0 + 1]) # '1'
list.append(string[0:1 + 1]) # '12'
list.append(string[0:2 + 1]) # '123'
# but wait...were is '13'???? (this is your hint!)
The next value of i is 1:
for j in range(1, 3):
# you would eventually execute all of the following
list.append(string[1:1 + 1]) # '2'
list.append(string[1:2 + 1]) # '23'
# notice how we are only grabbing values of position i or more?
Finally you get to i is 2:
for j in range(2, 3): # i.e. the whole string!
# you would eventually execute all of the following
list.append(string[2:2 + 1]) # '3'
I've shown you what is happening (as you've asked in your question), I leave it to you to devise your own solution. A couple notes:
You need to look at all index combinations from position i
Dont name objects by their type (i.e. dont name a list object list)

I would try something like this using itertools and powerset() recipe
from itertools import chain, combinations
def powerset(iterable):
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
output = list(map(''.join, powerset('123')))
output.pop(0)

Here is another option, using combinations
from itertools import combinations
def get_sub_ints(raw):
return [''.join(sub) for i in range(1, len(raw) + 1) for sub in combinations(raw, i)]
if __name__ == '__main__':
print(get_sub_ints('123'))
>>> ['1', '2', '3', '12', '13', '23', '123']

Related

Add counts to dict values

arr = [1,1,3,2,2,4]
d={i:[] for i in range(max(arr)+1)}
for i in arr:
if i in d.keys():
d[i] += 1
Am trying to get a result of {0:0, 1:2, 2:2, 3:1, 4:1} but get getting
TypeError: 'int' object is not iterable
Can any kind soul point to me what's the error? Thanks !
You might wanna do this:
arr = [1,1,3,2,2,4]
d = {i:0 for i in range(max(arr)+1)}
for i in arr:
if i in d.keys():
d[i] += 1
Or try more efficient way (you won't find keys which are not in array):
from collections import Counter
arr = [1,1,3,2,2,4]
d = Counter(arr)
You just need to change the part of the list comprehension to create 0 for each number until the max number instead of an empty list
arr = [1,1,3,2,2,4]
d={i:0 for i in range(max(arr) + 1)}
for i in arr:
if i in d.keys():
d[i] += 1
print(d)
Or to use dictionary comprehension with the range of max number in arr list and count of the list.
{v: arr.count(v) for v in range(max(arr) + 1)}
Output
{0: 0, 1: 2, 2: 2, 3: 1, 4: 1}

Compare lists with multiple elements

I have a tuple as follows s=[(1,300),(250,800),(900,1000),(1200,1300),(1500,2100)]
I need to compare the upper limit of the list with the lower limit of the next list. If the lower limit of the next list is less than the upper limit of the previous list than it should throw error else it should pass.
Example:
s=[(1,300),(250,800),(900,1000),(1200,1300),(1500,2100)] - This should throw error as 250<300.If it fails for any one, it should throw error immediately.
s=[(1,300),(350,800),(900,1000)] - This should not throw error as 350>300.
I have tried something like this:
s=[(1,300),(250,800),(900,1000)]
s= (sorted(s))
print(s)
def f(mytuple, currentelement):
return mytuple[mytuple.index(currentelement) + 1]
for i in s:
j = f(s,i)
if i[0]<j[1]:
print("fail")
else:
print("pass")
But it's not working. Help me out here.
zip() combines lists (or any iterables) to a new iterable. It stops when the shortest list is exhausted. Imagine:
a = [1, 2, 3, 4]
b = ['a', 'b', 'c']
zipped = zip(a, b) # Gives: [(1, 'a'), (2, 'b'), (3, 'c')]
# 4 is skipped, because there is no element remaining in b
We can used this to get all pairs in s in an elegant, easy to read form:
s=[(1,300),(250,800),(900,1000)]
s= (sorted(s))
pairs = zip(s, s[1:]) # zip s from index 0 with s from index 1
Now that we have pairs in the form of ((a0, a1), (b0, b1)) you can easily compare if a1 > b0 in a loop:
for a,b in pairs:
if a[1] > b[0]:
print("fail")
else:
print("pass")
Two problems I see:
1) You're running into an out of bounds error, as the last element (900,1000) is trying to check the follow element which does not exist.
You can skip the last element by adding [:-1] to your loop.
2) In addition, your "if" condition seems to be backwards. You seem to be wanting to compare i[1] with j[0] instead of i[0] with j[1].
s=[(1,300),(250,800),(900,1000)]
s= (sorted(s))
print(s)
def f(mytuple, currentelement):
return mytuple[mytuple.index(currentelement) + 1]
for i in s[:-1]:
j = f(s,i)
if i[1]>j[0]:
print("fail")
else:
print("pass")
See How to loop through all but the last item of a list? for more details.

Is there a way to index a list matrix using a single for loop?

I have a problem that involves taking a square matrix in list form and finding the absolute value of the difference between the primary diagonal and the secondary diagonal. For example, given the following list:
test_matrix = [[11, 2, 4], [4, 5, 6], [10, 8, -12]]
The correct solution returns
15
My solution which works is:
def diagonalDifference(test_matrix):
primary_diag = 0
secondary_diag = 0
for i, row in enumerate(test_matrix):
for j, val in enumerate(row):
if (i == j) and ((i + j) == len(test_matrix) - 1):
secondary_diag += val
primary_diag += val
elif i == j:
primary_diag += val
elif (i + j) == len(test_matrix) - 1:
print('secondary_diag:' + str(val))
secondary_diag += val
return abs(primary_diag - secondary_diag)
Which works fine but I was hoping if someone could tell me if there is a way to reproduce the solution using only one for loop. Any other improvements that can be made are also welcome.
Since you already have a core Python solution, let me offer a numpy-based solution that may be faster for large matrices:
import numpy as np
arr = np.array(test_matrix)
np.abs((np.diag(arr) - np.diag(np.fliplr(arr))).sum())
It is possible to solve the underlying problem in O(N), by simply iterating over possible row indices and using them to compute both row and column indices to index the (square) input data:
def diagonalDifference(test_matrix):
diff = 0
for i in range(len(test_matrix)):
diff += test_matrix[i][i]
diff -= test_matrix[i][-i-1]
return abs(diff)
test_matrix = [[11, 2, 4], [4, 5, 6], [10, 8, -12]]
print(diagonalDifference(test_matrix))

Fastest way to find all the indexes of maximum value in a list - Python

I am having list which as follows
input_list= [2, 3, 5, 2, 5, 1, 5]
I want to get all the indexes of maximum value. Need efficient solution. The output will be as follows.
output = [2,4,6] (The above list 5 is maximum value in a list)
I have tried by using below code
m = max(input_list)
output = [i for i, j in enumerate(a) if j == m]
I need to find any other optimum solution.
from collections import defaultdict
dic=defaultdict(list)
input_list=[]
for i in range(len(input_list)):
dic[input_list[i]]+=[i]
max_value = max(input_list)
Sol = dic[max_value]
You can use numpy (numpy arrays are very fast):
import numpy as np
input_list= np.array([2, 3, 5, 2, 5, 1, 5])
i, = np.where(input_list == np.max(input_list))
print(i)
Output:
[2 4 6]
Here's the approach which is described in comments. Even if you use some library, fundamentally you need to traverse at least once to solve this problem (considering input list is unsorted). So even lower bound for the algorithm would be Omega(size_of_list). If list is sorted we can leverage binary_search to solve the problem.
def max_indexes(l):
try:
assert l != []
max_element = l[0]
indexes = [0]
for index, element in enumerate(l[1:]):
if element > max_element:
max_element = element
indexes = [index + 1]
elif element == max_element:
indexes.append(index + 1)
return indexes
except AssertionError:
print ('input_list in empty')
Use a for loop for O(n) and iterating just once over the list resolution:
from itertools import islice
input_list= [2, 3, 5, 2, 5, 1, 5]
def max_indexes(l):
max_item = input_list[0]
indexes = [0]
for i, item in enumerate(islice(l, 1, None), 1):
if item < max_item:
continue
elif item > max_item:
max_item = item
indexes = [i]
elif item == max_item:
indexes.append(i)
return indexes
Here you have the live example
Think of it in this way, unless you iterate through the whole list once, which is O(n), n being the length of the list, you won't be able to compare the maximum with all values in the list, so the best you can do is O(n), which you already seems to be doing in your example.
So I am not sure you can do it faster than O(n) with the list approach.

Counter class extension

I am having a problem finding an elegant way to create a Counter() class that can:
Feed in arbitrary number of keys and return a nested dictionary based on this list of keys.
Increment for this nested dictionary is arbitrary as well.
For example:
counter = Counter()
for line in fin:
if a:
counter.incr(key1, 1)
else:
counter.incr(key2, key3, 2)
print counter
Ideally I am hoping to get the result looks like: {key1 : 20, {key2 : {key3 : 40}}}. But I am stuck in creating this arbitrary nested dictionary from list of keys. Any help is appreciated.
you can subclass dict and create your own nested structure.
here's my attempt at writing such class :
class Counter(dict):
def incr(self, *args):
if len(args) < 2:
raise TypeError, "incr() takes at least 2 arguments (%d given)" %len(args)
curr = self
keys, count = args[:-1], args[-1]
for depth, key in enumerate(keys, 1):
if depth == len(keys):
curr[key] = curr.setdefault(key, 0) + count
else:
curr = curr.setdefault(key, {})
counter = Counter()
counter.incr('key1', 1)
counter.incr('key2', 'key3', 2)
counter.incr('key1', 7)
print counter #{'key2': {'key3': 2}, 'key1': 8}
There are two possibilities.
First, you can always fake the nested-keys thing by using a flat Counter with a "key path" made of tuples:
counter = Counter()
for line in fin:
if a:
counter.incr((key1,), 1)
else:
counter.incr((key2, key3), 2)
But then you'll need to write a str-replacement—or, better, a wrapper class that implements __str__. And while you're at it, you can easily write an incr wrapper that lets you use exactly the API you wanted:
def incr(self, *args):
super().incr(args[:-1], args[-1])
Alternatively, you can build your own Counter-like class on top of a nested dict. The code for Counter is written in pure Python, and the source is pretty simple and readable.
From, your code, it looks like you don't have any need to access things like counter[key2][key3] anywhere, which means the first is probably going to be simpler and more appropriate.
The only type of value that can exist in a Counter object is an int, you will not be able to represent a nested dictionary with a Counter.
Here is one way to do this with a normal dictionary (counter = {}). First, to update increment the value for a single key:
counter[key1] = counter.setdefault(key1, 0) + 1
Or for an arbitrary list of keys to create the nested structure:
tmp = counter
for key in key_list[:-1]:
tmp = tmp.setdefault(key, {})
tmp[key_list[-1]] = tmp.setdefault(key_list[-1], 0) + 1
I would probably turn this into the following function:
def incr(counter, val, *keys):
tmp = counter
for key in keys[:-1]:
tmp = tmp.setdefault(key, {})
tmp[keys[-1]] = tmp.setdefault(keys[-1], 0) + val
Example:
>>> counter = {}
>>> incr(counter, 1, 'a')
>>> counter
{'a': 1}
>>> incr(counter, 2, 'a')
>>> counter
{'a': 3}
>>> incr(counter, 2, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 2}}}
>>> incr(counter, 3, 'b', 'c', 'd')
>>> counter
{'a': 3, 'b': {'c': {'d': 5}}}

Resources