Python List Comprehension With Equation - python

I need to turn this into a list comprehension. I have been stuck for a while. Any Ideas?
result = []
for i in range(length + 1):
m = number * i
result.append(m)
del result[0]
return result

You can do this -
[number*i for i in range(length+1)][1:]
This iterates over the range(length+1) multiplying each to the number and storing in a list. Then I just take all the elements except the 0th.
You can start iterating from 1 to avoid generate 0th element in the first place, as suggested by #wwii
[number*i for i in range(1,length+1)] #or simply range(length)

Related

Given a list of numbers return a list in which all equal adjacent elements have been reduced to a single element

I need to write a code that Given a list of numbers return a list in which all
equal adjacent elements have been reduced
to a single element.
example:
list=[1,1,3,4,5,5,6,7,7,7,8,5,7]
becomes
[1,3,4,5,6,7,8,5,7]
i wrote this code:
list = []
list1 = []
for i in range(10):
c = input("inserisci un numero: ")
list.append(c)
k = 0
for i in range(len(list)-1):
if list[i+1] != list[i]:
list1[k].append(list[i])
k+=1
print(list1)
ut it's giving me index error could you explain me why?
You want list1.append(list[i]), not list1[k]. The k variable is useless.
The reason it explodes is that, the first time through the second loop, you refer to list1[0], but list1 is empty. It has no elements, so index 0 is out of bounds.
Add the first element to result list
result.append(list[0])
then just loop from 1 to the end of the original list.
On each iteration just check if list[i-1] != list[i]: if true add to result list like this result.append(list[i])
Using itertools.groupby function from the standard library you can group similar terms.
import itertools as it
l = [1,1,3,4,5,5,6,7,7,7,8,5,7]
ll = []
_ = [ll.append(next(grp)) if check else ll.append(grp) for check, grp in it.groupby(l)]
print(ll)

Swapping List Elements During Iteration

I have read in several places that it is bad practice to modify an array/list during iteration. However many common algorithms appear to do this. For example Bubble Sort, Insertion Sort, and the example below for finding the minimum number of swaps needed to sort a list.
Is swapping list items during iteration an exception to the rule? If so why?
Is there a difference between what happens with enumerate and a simple for i in range(len(arr)) loop in this regard?
def minimumSwaps(arr):
ref_arr = sorted(arr)
index_dict = {v: i for i,v in enumerate(arr)}
swaps = 0
for i,v in enumerate(arr):
print("i:", i, "v:", v)
print("arr: ", arr)
correct_value = ref_arr[i]
if v != correct_value:
to_swap_ix = index_dict[correct_value]
print("swapping", arr[to_swap_ix], "with", arr[i])
# Why can you modify list during iteration?
arr[to_swap_ix],arr[i] = arr[i], arr[to_swap_ix]
index_dict[v] = to_swap_ix
index_dict[correct_value] = i
swaps += 1
return swaps
arr = list(map(int, "1 3 5 2 4 6 7".split(" ")))
assert minimumSwaps(arr) == 3
An array should not be modified while iterating through it, because iterators cannot handle the changes. But there are other ways to go through an array, without using iterators.
This is using iterators:
for index, item in enumerate(array):
# don't modify array here
This is without iterators:
for index in range(len(array)):
item = array[index]
# feel free to modify array, but make sure index and len(array) are still OK
If the length & index need to be modified when modifying an array, do it even more "manually":
index = 0
while index < len(array):
item = array[index]
# feel free to modify array and modify index if needed
index += 1
Modifying items in a list could sometimes produce unexpected result but it's perfectly fine to do if you are aware of the effects. It's not unpredictable.
You need to understand it's not a copy of the original list you ar iterating through. The next item is always the item on the next index in the list. So if you alter the item in an index before iterator reaches it the iterator will yield the new value.
That means if you for example intend to move all items one index up by setting item at index+1 to current value yielded from enumerate(). Then you will end up with a list completely filled with the item originally on index 0.
a = ['a','b','c','d']
for i, v in enumerate(a):
next_i = (i + 1) % len(a)
a[next_i] = v
print(a) # prints ['a', 'a', 'a', 'a']
And if you appending and inserting items to the list while iterating you may never reach the end.
In your example, and as you pointed out in a lot of algorithms for e.g. combinatoric and sorting, it's a part of the algorithm to change the forthcoming items.
An iterator over a range as in for i in range(len(arr)) won't adapt to changes in the original list because the range is created before starting and is immutable. So if the list has length 4 in the beginning, the loop will try iterate exactly 4 times regardless of changes of the lists length.
# This is probably a bad idea
for i in range(len(arr)):
item = arr[i]
if item == 0:
arr.pop()
# This will work (don't ask for a use case)
for i, item in enumerate(arr):
if item == 0:
arr.pop()

Make the code run faster without nested loop

I am trying to solve assignment problem, the code I wrote takes extremely long to run. I think it's due to nested loop I used. Is there another way to rewrite the code to make it more efficient.
The question I am trying to solve. Basically, starting at first element to compare with every element to its right. if it is larger than the rest, it will be "dominator". Then the second element to compare with every element to its right again. All the way to the last element which will be automatically become "dominator"
def count_dominators(items):
if len(items) ==0:
return len(items)
else:
k = 1
for i in range(1,len(items)):
for j in items[i:]:
if items[i-1]>j:
k = k+1
else:
k = k+0
return k
You can use a list comprehension to check if each item is a "dominator", then take the length - you have to exclude the final one to avoid taking max of an empty list, then we add 1 because we know that the final one is actually a dominator.
num_dominators = len([i for i in range(len(items) - 1) if items[i] > max(items[i + 1:])]) + 1
This is nice because it fits on one line, but the more efficient (single pass through the list) way to do it is to start at the end and every time we find a new number bigger than any we have seen before, count it:
biggest = items[-1]
n = 1
for x in reversed(items):
if x > biggest:
biggest = x
n+=1
return n

Replace for with list comprehension

In this script I have used both list comprehension and for. I need to replace for loop with comprehension and add this solve inside list comprehension.
How can add
for i in k:
count_list.append(l.count(i))
inside this block
pairs = [int(pair/2) for pair in count_list if int(pair/2) != 0]
My code:
def sockMerchant(ar):
l = ar
k = set(l)
count_list = []
for i in k:
count_list.append(l.count(i))
pairs = [int(pair/2) for pair in count_list if int(pair/2) != 0]
return sum(pairs)
n = int(input().strip())
ar = list(map(int, input().strip().split(' ')))
result = sockMerchant(ar)
print(result)
You should not be using a list comprehension at all, nor the for loop you have now. The loop is inefficient; by using list.count() you are traversing the whole list l for every unique value, creating a O(N^2) loop.
Use a collections.Counter() object instead and count in O(N) time:
from collections import Counter
def sockMerchant(ar):
counts = Counter(ar)
return sum(count//2 for count in counts.values())
or even
def sockMerchant(ar):
return sum(count//2 for count in Counter(ar).values())
if you insist on a single line.
Note that sum() doesn't mind a few 0 values here and there, so I removed the if test for single 'socks'. Also, I used the // floor division operator rather than turning the floating point result of dividing by 2 back into an integer.

Comparing adjacent elements together without using zip method

How would you go about comparing two adjacent elements in a list in python? How would save or store the value of that item while going through a for loop? I'm trying not to use the zip method and just using an ordinary for loop.
comparing_two_elements = ['Hi','Hello','Goodbye','Does it really work finding longest length of string','Jet','Yes it really does work']
longer_string = ''
for i in range(len(comparing_two_elements)-1):
if len(prior_string) < len(comparing_two_elements[i + 1]):
longer_string = comparing_two_elements[i+1]
print(longer_string)
The below works simply by 'saving' the first element of your list as the longest element, as it will be the first time you loop over your list, and then on subsequent iterations it will compare the length of that item to the length of the next item in the list.
longest_element = None
for element in comparing_two_elements:
if not longest_element:
longest_element = element
continue
if len(longest_element) < len(element):
longest_element = element
If you want to go the "interesting" route, you could do it with combination of other functions, for eg
length_map = map(len, comparing_two_elements)
longest_index = length_map.index(max(length_map))
longest_element = comparing_two_elements[longest_index]
Use the third, optional step argument to range - and don't subtract 1 from len(...) ! Your logic is incomplete: what if the first of a pair of strings is longer? you don't do anything in that case.
It's not clear what you're trying to do. This for loop runs through i = 0, 2, 4, ... up to but excluding len(comparing_two_elements) (assumed to be even!), and prints the longer of each adjacent pair:
for i in range(0, len(comparing_two_elements), 2):
if len(comparing_two_elements[i]) < len(comparing_two_elements[i + 1]):
idx = i
else:
idx = i + 1
print(comparing_two_elements[idx])
This may not do exactly what you want, but as several people have observed, it's unclear just what that is. At least it's something you can reason about and adapt.
If you just want the longest string in a sequence seq, the whole adjacent pairs rigamarole is pointless; simply use:
longest_string = max(seq, key=len)

Categories

Resources