How to get index of the last item with a given value - python

In a list of integer values
a = [4, 8, 2, 3, 8, 5, 8, 8, 1, 4, 8, 2, 1, 3]
I have to find the index of the last item with value 8. Is there more elegant way to do that rather than mine:
a = [4, 8, 2, 3, 8, 5, 8, 8, 1, 4, 8, 2, 1, 3]
for i in range(len(a)-1, -1, -1):
if a[i] == 8:
print(i)
break

Try This:
a = [4, 8, 2, 3, 8, 5, 8, 8, 1, 4, 8, 2, 1, 3]
index = len(a) - 1 - a[::-1].index(8)
Just reverse the array and find first index of element and then subtract it from length of array.

>>> lst = [4, 8, 2, 3, 8, 5, 8, 8, 1, 4, 8, 2, 1, 3]
>>> next(i for i in range(len(lst)-1, -1, -1) if lst[i] == 8)
10
This throws StopIteration if the list doesn't contain the search value.

Use sorted on all indexes of items with a value of 8 and take the last value, or using reverse = True take the first value
x = sorted(i for i, v in enumerate(a) if v == 8)[-1]
print(x) # => 10

Related

How to slice list based on a condition that every element of another list must appear atleast once?

I have two lists :
a = [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2, 1, 3, 5, 7, 0]
key = [1, 2, 4, 6]
I want to check if all elements in the key have atleast once appeared in the list a and remove the ones after that.
desired output :
a = [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2]
here is what i tried:
if a[-1] not in key:
indx = -1
while indx < 0:
if a[indx] in k:
ind = indx
indx = 1
else: indx= indx-1
a = a[:ind+1]
but this just check if the last element of a is in key. Idk how to check for the condition if all the key elements have appeared atleast once. Can some help ?
Here is an efficient solution that is O(n) and doesn't require slicing lists or a list.index operation in a loop:
First, create a dictionary mapping each element of a to the index of its first occurrence.
a = [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2, 1, 3, 5, 7, 0]
a_lookup = dict()
for ind, val in enumerate(a):
if val not in a_lookup:
a_lookup[val] = ind
This gives us
a_lookup = {3: 0, 8: 1, 5: 2, 1: 3, 4: 4, 7: 5, 6: 8, 2: 10, 0: 15}
Next, find the largest value in the dictionary, for all keys in the key list. If we use dict.get to get the keys, a non-existent key will return None, which will cause a TypeError in the max call. We can catch this and handle it appropriately. Once we've found the maximum index, slice the list until this index to get what we need.
key = [1, 2, 4, 6]
try:
max_index = max(a_lookup.get(k) for k in key)
sliced_list = a[:max_index+1]
except TypeError:
print("Error: all keys do not exist in a")
which gives,
sliced_list = [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2]
This function slices the list based on the condition that every element of the key must appear at least once in a.
def slice_list(a, key):
for i in range(len(a)): # iterate over the list
if a[i] in key: # check if the element is in the key
key.remove(a[i]) # remove the element from the key
if not key: # if the key is empty
return a[: i + 1] # return the sliced list
return a # if the key is not empty return the original list
print(slice_list(a, key))
Output: [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2]
Try:
a = [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2, 1, 3, 5, 7, 0]
key = [1, 2, 4, 6]
max_idx = max(a.index(k) for k in key)
print(a[: max_idx + 1])
Prints:
[3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2]
Another method to get the same result :)
for i in range(len(a)):
if all(x in a[:i] for x in key):
b = a[:i]
break
print(b)
Output: [3, 8, 5, 1, 4, 7, 1, 3, 6, 8, 2]

Compare two lists and get the indices where the values are different

I would like help with the following situation
I have two lists:
Situation 1:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [0, 1, 2, 3, 5, 5, 6, 7, 8, 9]
I need key output: Key 4 is different
Situation 2:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
I need key output: false -> no key is different
Situation 3:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [0, 9, 2, 3, 4, 5, 6, 7, 3, 9]
I need key output: Key 1 and Key 8 is different
How could I resolve this? My array has 260 keys
You can use a list comprehension with zip, and enumerate to get the indices. Use short-circuiting and the fact that an empty list is falsy to get False:
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [0, 1, 2, 3, 5, 5, 6, 7, 8, 9]
out = [i for i,(e1,e2) in enumerate(zip(a,b)) if e1!=e2] or False
output:
[4]
output for example #2: False
output for example #3: [1, 8]
An approach with itertools.compress. Compare the lists to check difference in values, pass the result of the comparison to compress which will pick-up only the True ones.
from itertools import compress
a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
b = [0, 1, 2, 3, 5, 5, 6, 7, 8, 9]
result = tuple(compress(a, map(int.__ne__, a, b)))
if not result:
print(False)
else:
print(result)
The current answer is really good, but for an approach which doesn't need zip/enumerate, loop like this:
lista = []
listb = []
unequal_keys = []
for idx in range(len(lista)):
if lista[idx] != listb[idx]:
unequal_keys.append(idx)
if unequal_keys == []:
print(False)
else:
print(unequal_keys)
I recommend learning new techniques and using build ins like zip and enumerate though. They are much more pythonic and faster!

Printing a list after having removed adjacent duplicate elements

new_list = []
i = 0
def remove_adjacent(nums):
global i
while i < len(nums) - 1:
if nums[i] != nums[i+1]:
new_list.append(nums[i])
else:
i += 1
remove_adjacent(nums[i:])
return
i += 1
l = [1, 2, 3, 5, 4, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9, 8, 8]
remove_adjacent(l)
print new_list
Question: Given a list of numbers, return a list where all adjacent == elements have been reduced to a single element, so [1, 2, 2, 3] returns [1, 2, 3]. You may create a new list or modify the passed in list.
Issue: The final list printed consists of [1, 2, 3, 5] instead of [1, 2, 3, 5, 4, 5, 6, 7, 8, 9, 8]
What you would want is a problem best solved by itertools.groupby
l
Out[35]: [1, 2, 3, 5, 4, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9, 8, 8]
from itertools import groupby
[k for k, _ in groupby(l)]
Out[36]: [1, 2, 3, 5, 4, 5, 6, 7, 8, 9, 8]
What itertools.groupby does is, it groups consecutive keys together by generating a tuple of the element and the consecutive group as a list
To get a clear understanding of itertools.groupby, you can dump the resultant list of tuples generated by grouping the list of consecutive numbers
[(k, list(g)) for k, g in groupby(l)]
Out[40]:
[(1, [1]),
(2, [2]),
(3, [3]),
(5, [5]),
(4, [4, 4]),
(5, [5, 5]),
(6, [6]),
(7, [7, 7, 7]),
(8, [8, 8]),
(9, [9]),
(8, [8, 8])]
new_list = []
def remove_adjacent(nums):
i = 0
while i < len(nums) - 1:
if nums[i] != nums[i+1]:
new_list.append(nums[i])
else:
i += 1
remove_adjacent(nums[i:])
return
i += 1
l = [1, 2, 3, 5, 4, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9, 8, 8]
remove_adjacent(l)
# appending the last item
new_list.append(l[len(l)-1])
print (new_list.append(nums[len(nums) - 1]))
Output
[1, 2, 3, 5, 4, 5, 6, 7, 8, 9, 8]
This is perfect for a generator. I'm not altering the original list. Instead, I'm returning a new list with no adjacent values equal to one another.
def removerator(l):
last = None
for x in l:
if x != last:
last = x
yield x
list(removerator(l))
[1, 2, 3, 5, 4, 5, 6, 7, 8, 9, 8]
Setup
l = [1, 2, 3, 5, 4, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9, 8, 8]
I made a function where it get a list and iterate over its items, then it adds the items that aren't the same as the items already added to the list in the previous iteration.
l = [1, 2, 3, 5, 4, 4, 5, 5, 6, 7, 7, 7, 8, 8, 9, 8, 8] # List that we want to change
def remove_adjacent(l): # Define a new function and accept an argument: the list to check.
new = [l[0]] # Make a new list (temporal) and assing like it's first item the first item of the main list. It's the same as new = [] and new.append(l[0]).
for item in l[1:]: # We iterate across the list, but we don't iterate on the first item because we've alreaday added it to the list, if you want you can delete the slice in [1:] since it will only make the iteration a really small fraction more slowly.
if new[-1] != item: # We check if the new item is the same as the last item added to the new list, if not, we add it.
new.append(item) # We add the item to the new list.
return new # We return the list.
print(remove_adjacent(l)) # We check it.
# [1, 2, 3, 5, 4, 5, 6, 7, 8, 9, 8]

Duplicate problems with Selection Sort

If I have a list where all values are unique, the code runs fine. However, if there is a duplicate value within the list, when finding the minimum value for the next iteration, it pulls from the entire list rather than just the remainder of the list.
for n in range(0,len(lst)):
a = min(lst[n:]) #minimum value within remainder of set
i = lst.index(a) #index value for minimum value within remainder of set
temp = lst[n]
lst[n] = a
lst[i] = temp
Results look like this:
lst = [6, 8, 9, 1, 3, 4, 7, 5, 4]
[1, 8, 9, 6, 3, 4, 7, 5, 4]
[1, 3, 9, 6, 8, 4, 7, 5, 4]
[1, 3, 4, 6, 8, 9, 7, 5, 4]
[1, 3, 6, 4, 8, 9, 7, 5, 4]
[1, 3, 6, 8, 4, 9, 7, 5, 4]
[1, 3, 6, 8, 9, 4, 7, 5, 4]
[1, 3, 6, 8, 9, 7, 4, 5, 4]
[1, 3, 6, 8, 9, 7, 5, 4, 4]
[1, 3, 6, 8, 9, 7, 5, 4, 4]
I'm looking for it to return this:
[1, 3, 4, 4, 5, 6, 7, 8, 9]
When n is 4, the next minimum is 4 again, but lst.index() finds the first 4 at position 3 instead.
Start searching for the miminum from n; the .index() method takes a second argument start, from where to start searching:
i = lst.index(a, n)
Note that Python can assign to two targets in place, no need to use a temporary intermediate. range() with just one argument starts from 0:
for n in range(len(lst)):
a = min(lst[n:])
i = lst.index(a, n)
lst[n], lst[i] = a, lst[n]
Demo:
>>> lst = [6, 8, 9, 1, 3, 4, 7, 5, 4]
>>> for n in range(0,len(lst)):
... a = min(lst[n:])
... i = lst.index(a, n)
... lst[n], lst[i] = a, lst[n]
...
>>> lst
[1, 3, 4, 4, 5, 6, 7, 8, 9]

convert an int to list of individual digitals more faster?

All,
I want define an int(987654321) <=> [9, 8, 7, 6, 5, 4, 3, 2, 1] convertor, if the length of int number < 9, for example 10 the list will be [0,0,0,0,0,0,0,1,0]
, and if the length > 9, for example 9987654321 , the list will be [9, 9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> i
987654321
>>> l
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> z = [0]*(len(unit) - len(str(l)))
>>> z.extend(l)
>>> l = z
>>> unit
[100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10, 1]
>>> sum([x*y for x,y in zip(l, unit)])
987654321
>>> int("".join([str(x) for x in l]))
987654321
>>> l1 = [int(x) for x in str(i)]
>>> z = [0]*(len(unit) - len(str(l1)))
>>> z.extend(l1)
>>> l1 = z
>>> l1
[9, 8, 7, 6, 5, 4, 3, 2, 1]
>>> a = [i//x for x in unit]
>>> b = [a[x] - a[x-1]*10 for x in range(9)]
>>> if len(b) = len(a): b[0] = a[0] # fix the a[-1] issue
>>> b
[9, 8, 7, 6, 5, 4, 3, 2, 1]
I tested above solutions but found those may not faster/simple enough than I want and may have a length related bug inside, anyone may share me a better solution for this kinds convertion?
Thanks!
Maybe I am missing something, but shouldn't this be enough (without value checking)?
def int_to_list(i):
return [int(x) for x in str(i).zfill(9)]
def list_to_int(l):
return int("".join(str(x) for x in l))
Reference: str.zfill
And what about :
def int_to_list(num)
return list ("%010d" % num)
def convert(number):
stringified_number = '%s' % number
if len(stringified_number) < 9:
stringified_number = stringified_number.zfill(9)
return [int(c) for c in stringified_number]
>>> convert(10)
[0, 0, 0, 0, 0, 0, 0, 1, 0]
>>> convert(987654321)
[9, 8, 7, 6, 5, 4, 3, 2, 1]
To place an integer of any length into a list in sequence by integer digit -
a = 123456789123456789123456789123456789123456789123456789
j = len('{}'.format(a))
b = [0 for i in range(j)]
c = 0
while j > 0:
b [c] = a % 10**j // 10**(j-1)
j = j-1
c = c + 1
print(b)
output -
[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9]
you can put the condition on j for the alternate assignment to b.

Categories