Iterating over a list until 2 conditions are met - python

I have a list, and I want to sum all the numbers in that list ...
except that if a 6 comes up - that is not counted and any number from that 6, until a next 7 comes up (also not counting the 7). A 7 always will appear somewhere after a 6.
For example:
my_list = [1,2,3,6,1,1,1,7,2,2,2]
1,2,3,.........,2,2,2 # Omit numbers from the first 6 to the next 7.
should output 12.
I know how to identify the 6, I'm just not sure how to not count the numbers until a followup 7 comes.
Thank you.

You can use a boolean as a flag. This should do it:
list= [1,2,3,6,1,1,1,7,2,2,2]
do_sum = True
total_sum = 0
for item in list:
if item == 6:
do_sum = False
if do_sum:
total_sum += item
if not do_sum and item == 7:
do_sum = True
The last if will check if the 6 went before the 7. So it will sum any seven that appears before a 6.
This solution supports multiple cases of sixes and sevens pairs in the list.

Let's do this as we would on paper:
Find the first 6; mark the list up to that point.
In the rest of the list, find the first 7; mark the list after that point.
Combine the two marked list portions; sum those elements.
Code, with a line of tracing output:
seq = [1, 2, 3, 6, 1, 1, 1, 7, 2, 2, 2]
first6 = seq.index(6)
rest = seq[first6:]
next7 = rest.index(7)
sum_list = seq[:first6] + rest[next7+1:]
print("Add these:", sum_list)
print("Sum:", sum(sum_list))
Output:
Add these: [1, 2, 3, 2, 2, 2]
Sum: 12
You can shorten the code by combining expressions, but I think this is more readable for you at this stage of your programming career.

Related

Loop through list to extract specific patterns

I have a quite specific question that I'm unsure about how to go forward with.
I have a list of numbers and I want to extract some specific patterns from them where I loop through the list and create a new one, it's easier to explain with an example.
Say I have a list, a = [2, 9, 3, 2, 3, 5, 7, 9].
What I want to do with this list is loop through 4 numbers at a time and give them corresponding letters, depending on when they occur in the sequence.
i.e. First four numbers = 2932 = ABCA
Second sequence of numbers = 9323 = ABCB
Third sequence = 3235 = ABAC
Fourth sequence = 2357 = ABCD
Fifth sequence = 3579 = ABCD
I then want to take these sequences and add them to another list which would now look like,
b = [ABCA, ABCB, ABAC, ABCD, ABCD]
I'm really unsure about how the format of the code should be, the length of the new list will always be 3 less than the original. Any help would be great, thanks.
You can use a dictionary to assign letters to numbers and read that dictionary again to access the relevant letters. It has 2 loops, which is not ideal but it does the job:
a = [2, 9, 3, 2, 3, 5, 7, 9]
len_a = len(a)
output = []
letters = 'ABCD'
for i in range(len_a-3):
d = {}
k = 0
for j in a[i:i+4]:
if j not in d:
d[j] = letters[k]
k += 1
else:
continue
letter_assignment = ''.join([d[j] for j in a[i:i+4]])
output.append(letter_assignment)
Output:
print(output)
# ['ABCA', 'ABCB', 'ABAC', 'ABCD', 'ABCD']
I recommend using zip function for corresponding the numbers with the letters, while for the loop, use the "for" function.

How to remove some elements from a list and append them at the beginning of the list in Python

Suppose that I have a list that has [0, 1, 2, 3 , 4, 5, 6] in it. I want to remove those elements that are greater than or equal to 3 and add those removed elements to the beginning of the list. So I wrote the code below:
list = [0, 1, 2, 3, 4, 5, 6]
new_list =[]
for number in list:
if number >= 3:
dropped_number = list.pop()
new_list.append(dropped_number)
new_list.sort()
new_list += list
print(new_list)
However, when I ran the code, the result was displayed as [5, 6, 0, 1, 2, 3 , 4]. Could anyone please explain to me at which step I did wrong here?
There are two issues with your code.
the number you obtain with list.pop() is not the one you just checked with your condition (it is merely the last one in the list)
When you reach 3, list.pop() removes 6,
When you reach 4, list.pop() removes 5,
You never reach 5 because you're at the end of what remains of the list at that point.
removing items from a list within a for-loop on the same list will cause the for-loop to skip items or complain that the list changed during iterations. So, even if you were to pop the appropriate number, your loop would miss items.
You also don't need to sort new_list every time you add to it, you can do it once at the end, but that just optimization.
Instead of a for-loop, you could use the sort method with a key parameter that returns a boolean indicating True for elements that do not meet your conditions (i.e that will be shifted to the right). Because Python's sort is stable, this will only place elements in two groups without otherwise changing their relative order.
L = [0, 2, 4, 6, 1, 3, 5]
L.sort(key=lambda x: not x>=3)
print(L) # [4, 6, 3, 5, 0, 2, 1]
If you need a more procedural solution, you can separate the values in two lists that you stick together at the end:
L = [0, 2, 4, 6, 1, 3, 5]
left,right = [], []
for x in L:
if x >= 3: left.append(x)
else: right.append(x)
L = left + right
# [4, 6, 3, 5, 0, 2, 1]
Modifying a list while iterating over it is usually problematic. What if instead you thought of the problem as building a new list out of two subsets of the original list?
>>> old_list = list(range(7))
>>> [i for i in old_list if i >= 3] + [i for i in old_list if i < 3]
[3, 4, 5, 6, 0, 1, 2]
The reason your program doesn't work is because you are modifying the list whilst searching through it. Instead, you can start by adding the elements >= 3 to a new list and then separately appending the elements < 3 to the list. Also, considering you are created a second 'new_list', there is no need to remove the elements from the first list.
Your new code:
list = [0, 1, 2, 3, 4, 5, 6]
new_list = []
# Append numbers greater than 3 to the new list
for number in list:
if number >= 3:
new_list.append(number)
# Append the numbers less than 3 to the new list
new_list += list[0:list.index(new_list[0])]
print(new_list)
Just to note, this method takes a section of the original list from position 0, to the position (.index) of the first item in the new list, which automatically generates the < 3 condition as the first item in the new list corresponds to the items before the >= 3 condition is met.
list[0:list.index(new_list[0])]

Identify least common number in a list python

With a list of numbers, each number can appear multiple times, i need to find the least common number in the list. If different numbers have the same lowest frequency, the result is the one occurring last in the list. An example, the least common integer in [1, 7, 2, 1, 2] is 7 (not 2, as originally said). And the list needs to stay unsorted
I have the following but it always sets the last entry to leastCommon
def least_common_in_unsorted(integers):
leastCommon = integers[0]
check = 1
appears = 1
for currentPosition in integers:
if currentPosition == leastCommon:
appears + 1
elif currentPosition != leastCommon:
if check <= appears:
check = 1
appears = 1
leastCommon = currentPosition
return leastCommon
Any help would be greatly appreciated
It is the simplest way come in my mind right now:
a = [1, 7, 2, 1, 2]
c, least = len(a), 0
for x in a:
if a.count(x) <= c :
c = a.count(x)
least = x
least # 7
and in two least items it will return the last occurrence one.
a = [1, 7, 2, 1, 2, 7] # least = 7
Using the Counter:
from collections import Counter
lst = [1, 7, 2, 1, 2]
cnt = Counter(lst)
mincnt = min(cnt.values())
minval = next(n for n in reversed(lst) if cnt[n] == mincnt)
print(minval) #7
This answer is based on #offtoffel to incorporate multiple items of the same number of occurrences while choosing the last occurring one:
def least_common(lst):
return min(lst, key=lambda x: (lst.count(x), lst[::-1].index(x)))
print(least_common([1,2,1,2]))
# 2
print(least_common([1,2,7,1,2]))
# 7
Edit: I noticed that there’s a even simpler solution, that is efficient and effective (just reverse the list in the beginning and min will keep the last value that has the minimum count):
def least_common(lst):
lst = lst[::-1]
return min(lst, key=lst.count)
print(least_common([1,2,1,2]))
# 2
print(least_common([1,2,7,1,2]))
# 7
Short but inefficient:
>>> min(a[::-1], key=a.count)
7
Efficient version using collections.Counter:
>>> min(a[::-1], key=Counter(a).get)
7
def least_common(lst):
return min(set(lst), key=lst.count)
Edit: sorry, this does not always take the last list item with least occurancy, as demanded by the user...it works on the example, but not for every instance.

Get previous value based in specific item

I am trying a script where we use a list item and then I will get the previous item.
lst = [1, 3, 4, 6, 8, 10]
next_item = next((x for x in lst if x > 6), None)
print next_item #8
This code works correctly for the item after 6. However i need the item before 6.
I am looking in docs for a prev method but i can't find anything related.
Any idea about that?
Assuming by "before" you mean "previous in lst", it is not necessary to use such a complicated way. This
lst[lst.index(6) - 1]
will give
4
while this
lst[lst.index(6) + 1]
will give
8
Of course you should check for index out of bound errors.
You can loop over the reversed list with a reverse condition :
>>> next((i for i in lst[::-1] if i<6),None)
4
If you are looking for the first number larger than 6 in lst, this is one way to do it:
lst = [1, 3, 4, 6, 8, 10]
lst2 =[]
for num in lst:
if num > 6:
lst2.append(num)
lst2.sort()
print lst2[0]
The output is 8

Trying to understand insertion sort algorithm

I'm reading some books on Python, data structures, and analysis and design of algorithms. I want to really understand the in's and out's of coding, and become an efficient programmer. It's difficult to ask the book to clarify, hence my question on stackoverflow. I'm really finding Algorithms and recursion challenging ... I posted some code (insertion sort) below that I'm trying to understand exactly what's happening. I understand, generally, what is supposed to happen, but I'm not really getting the how and why.
From trying to analyze pieces of the code on Python Idle, I know that:
key (holds variables) = 8, 2, 4, 9, 3, 6
and that:
i (holds the length) = 7 ( 1, 2, 3, 4, 5, 6, 7)
I don't know why 1 is used in the first line: range(1, len(mylist)). Any help is appreciated.
mylist = [8, 2, 4, 9, 3, 6]
for j in range(1,len(mylist)):
key = mylist[j]
i = j
while i > 0 and mylist[i-1] > key:
mylist[i] = mylist[i - 1]
i -= 1
mylist[i] = key
Let me try to break this down.
Start by considering a list. It is "almost" sorted. That is, the first few elements are sorted, but the last element is not sorted. So it looks something like this:
[10, 20, 30, 50, 15]
Obviously, the 15 is in the wrong place. So how do we move it?
key = mylist[4]
mylist[4] = mylist[3]
mylist[3] = key
That'll switch around the 15 and the 50 so now the list looks like:
[10, 20, 30, 15, 50]
But we'd like to do this several times in a loop. To do that we can do:
while ???:
key = mylist[i]
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
That loop will go back one position at a time swapping the two elements. That'll move the out of order position one place back each time. But how do we know when to stop?
Let's look again at our list and the moves we want to make:
[10, 20, 30, 50, 15]
[10, 20, 30, 15, 50]
[10, 20, 15, 30, 50]
[10, 15, 20, 30, 50]
# stop! we are sorted now!
But what is different that last time around? Every time we move the number one place back, it is because the 15 is less then the element on the left, meaning its not sorted. When that is no longer true we should stop moving. But we can easily deal with that:
key = mylist[i]
while key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
Ok, but happens if we now try to sort this list:
[10, 20, 1]
[10, 1, 20]
[1, 10, 20]
# ERROR!!
At this point something bad happens. We try to check whether key < mylist[i-1] but when we've reached the beginning, i = 0, and this checks the end of the list. But we should stop moving to left at this point...
If we reach the beginning of the list, we can't move our pivot/key further so we should stop. We update our while loop to handle that:
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
So now we have a technique for sorting an almost sorted list. But how can we use that to sort a whole list? We sort parts of the list at a time.
[8, 2, 4, 9, 3, 6]
First we sort the first 1 elements:
[8, 2, 4, 9, 3, 6]
Then we sort the first 2 elements:
[2, 8, 4, 9, 3, 6]
Then we sort the first 3 elements
[2, 4, 8, 9, 3, 6]
So on and so forth
[2, 4, 8, 9, 3, 6]
[2, 4, 8, 9, 3, 6]
[2, 3, 4, 8, 9, 6]
[2, 3, 4, 6, 8, 9]
But how do we do we do that? With a for loop
for j in range(len(mylist)):
i = j
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
But we can skip the first time through, because a list of one element is obviously already sorted.
for j in range(1, len(mylist)):
i = j
key = mylist[i]
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
mylist[i-1] = key
i -= 1
A few minor changes which make no difference brings us back to your original code
for j in range(1, len(mylist)):
key = mylist[j]
i = j
while i > 0 and key < mylist[i-1]:
mylist[i] = mylist[i-1]
i -= 1
mylist[i] = key
The insertion sort algorithm works by trying to build up a sorted list of increasing length at the start of the array. The idea is that if you start off by building a one-element sorted list at the beginning, then a two-element list, then a three-element list, etc., that once you've built up an n-element sorted list, you have sorted the entire array and are done.
For example, given the array
3 1 4
We can split this into a zero-element sorted list and a three-element unsorted list:
| 3 1 4
Now, we add 3 to our sorted list. Since that list is now only one element long, it's automatically sorted:
3 | 1 4
Now, we want to add 1 to our sorted list. If we just add 1 to the end of the list like this:
3 1 | 4
then the sorted list is no longer sorted. To fix this, the inner loop of the insertion sort code works by continuously swapping the 1 with the element before it until it's in the proper position. In our case, we swap the 1 and the 3:
1 3 | 4
and since the 1 is now at the beginning of the array, we don't need to move it any more. This is why the inner loop runs while i > 0; once the index of the new element (i) is at the start of the array, there's nothing before it that could be any bigger.
Finally, we update the array by adding 4 to the sorted list. Since it's in sorted position, we're done:
1 3 4
And our array is now in sorted order.
Now, to your original question: why does the outer loop start at 1? This is a cute optimization trick. The idea is that any one-element array must automatically be sorted. This means that the algorithm can start off by saying that the first element of the array is a one-element sorted list. For example, given the array
2 7 1 8
The insertion sort algorithm could try splitting this array like this, putting an empty sorted list at the front:
| 2 7 1 8
But a marginally faster option is to split the list like this:
2 | 7 1 8
which is guaranteed to be safe because any one-element list is automatically sorted.
This is really an optimization of the algorithm on the part of the authors. The algorithm would work perfectly fine if the outer loop started at zero, but they've just decided to start it at one to avoid an unnecessary loop iteration.
Hope this helps!
Have a look at the while loop. It starts with i having the value of 1, but then i is decreased. So in the last line, the minimum value of i could be 0, which is the first element in the list. If you would start with 0, i would become -1 which is valid in python, but means the last element. Therefore the range has to start with 1.
I would like to mention, that you are asking for insertion sort. I don't thin that your code implements insertion sort. Looks rather like bubble sort or something like that.
The reason is that:
i = j
and that mylist is accessed like:
mylist[i - 1]
Therefor the first value is 0. If the range would have started at 0, it would cause an mylist to be accessed at position -1.
Check out animated InsertionSort HERE
Later on i = j is set, and and myList[i-1] is accessed. So, j must be j >= 1.
Added: setting j = 0 is logicaly wrong because in the loop myList[j-1] is accessed - this is just by doing statical analysis of the code (and knowing i = j). Even if this cannot happen during runtime because of while i > 0, it is at least meaningless. If the expression myList[j-1] appears in the code, then it must surely be j >= 1.
The j-the iteration inserts the j-th element into the sorted elements before j. So it makes no sense to start with j=0. In the case j=1 the sublist below is myList[0:1] which is allways sorted, and the loop inserts myList[1] into the sublist myList[0:2]

Categories