How to pass an element in list as an index to a list in python? - python-3.x

Given a sorted list [1,2,3,4,5,6,7,8] and a shuffled version of that list
a=[1,2,5,3,7,8,6,4]
I want to find how many steps each element has moved from its position relative to the sorted list (steps towards index 0 being positive, steps away from index 0 being negative) and store those step values in the respective index in a different list.
For the given input, the expected output is:
b=[0,0,-1,-4,2,-1,2,2]
Considering the value 3 (which is at index 2 in the sorted list), in the shuffled array it is at index 3, so it has taken -1 step, and hence b[2] = -1. Similarly for the value 8 (which is at index 7 in the sorted list), in the shuffled array it is at index 5, so it has taken +2 steps, and hence b[7] = 2.
This is what I have tried:
b=[0,0,0,0,0,0,0,0]
a=[1,2,5,3,7,8,6,4]
for x in range(len(a)):
b[a[x]]=a[x]-(x+1)
I am getting an error with this code:
IndexError: list assignment index out of range
Please help me understand this. Why can't I pass an integer from a list as an index to another list?

You can get the result you want with a simple comparison of the expected value at an array position with the index of that value in the a array. By iterating over the number of elements in a, we can generate the output array:
a = [1,2,5,3,7,8,6,4]
b = []
for x in range(len(a)):
b.append(x - a.index(x+1))
print(b)
Output
[0, 0, -1, -4, 2, -1, 2, 2]
Demo on rextester

The problem as you very well know is that there is an index error.
a=[1,2,5,3,7,8,6,4]
The above array has size = 8.
Now you create b:
b=[0,0,0,0,0,0,0,0]
The size of the list b is also 8.
In your for loop you are doing:
b[a[x]]
Pause...
If x is 5, then a[x] will be 8. However, there is no such thing as index 8 in list b, which only has up to index 7.
This is the problem.
You should change the code in your for-loop to:
b[a[x] - 1]=a[x]-(x+1)
I would suggest you change your loop to this:
for x, n in enumerate(a):
b[n-1] = n-(x+1)

Index starts from 0 to n-1
,so in b=[0,0,0,0,0,0,0,0] (having 8 elements) index will start from 0 to 7 and list 'a=[1,2,5,3,7,8,6,4]' contains numbers from 1-8 hence 8 is out of index.
corrected code:
b=[0,0,0,0,0,0,0,0]
a=[1,2,5,3,7,8,6,4]
for x in range(len(a)):
b[a[x] - 1]=a[x]-(x + 1)

Avoiding any indexing, nor unnecessary offsets, we can store the elements in a dictionary (the element as the key and its move as the value)
a=[1,2,5,3,7,8,6,4]
d = {v: v-e for e,v in enumerate(a, 1)}
b = [v for _, v in sorted(d.items())]
print(b)
produces
[0, 0, -1, -4, 2, -1, 2, 2]

Related

Algorithm for calculating the duplicates in a list

The code below prints out the numbers which are duplicated in A.
From my understanding, the for loop goes through each element in the list and turns it into a negative number, though i can not figure out why it does not turn the numbers it prints (which are at at position 0,4,5) negative.
A = [1,2,3,1,3,6,6]
def printRepeating(arr, size):
print("The repeating elements are: ")
for i,x in enumerate(arr):
if arr[abs(arr[i])] >= 0:
arr[abs(arr[i])] = -arr[abs(arr[i])]
print(arr)
else:
print (abs(arr[i]), end = " ")
printRepeating(A,len(A))
The algorithm assumes:
all the elements of the array start as positive numbers, and
all the elements of the array are less than the length of the array.
In your example, since the length of the array is 7, all the elements in the array must be between 1 and 6.
What the algorithm does is change array[k] to negative to indicate that k has been seen. For example, since 1 is the first number seen, array[1] is changed to a negative number. The next time 1 is seen, array[1] is already negative, so 1 must be a duplicate.
If you just want to print the repeated values in the list then why not try this:
A = [1, 2, 3, 1, 3, 6, 6]
def get_repeated_elements(lst):
return list(set((i for i in lst if lst.count(i) > 1)))
print(get_repeated_elements(A))
This function converts the passed array into a generator of duplicated values
and then converts this into a set to filter out duplicates in the generator and then converts this into a list for returning to the caller. This is a far shorter function than the one given.
The algorithm assumes that all entries are strictly positive and smaller than the length of the list. Then, what it does is essentially using the sign of the i-th element to store if it already saw number i. In your example:
A=[1,2,3,1,3,6,6] Take 1
A[1] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,3,1,3,6,6] Take -2 (stands for 2)
A[2] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,-3,1,3,6,6] Take -3
A[3] is positive, i.e. we have not seen it. Make it negative to mark it
A=[1,-2,-3,-1,3,6,6] Take -1
A[1] is negative, i.e. we have already seen it. Report it.
A=[1,-2,-3,-1,3,6,6] Take 3
A[3] is negative, i.e. we have already seen it. Report it.
...
The below code can be used to find repeated elements in the list and also unique elements in the list.
from collections import Counter
A = [1,2,3,1,3,6,6]
B = Counter(A)
The below line prints repeated elements.
[k for k, v in B.items() if v > 1]
Output : [1, 3, 6]
The below line prints unique elements.
[k for k, v in B.items() if v == 1]
Output : [2]

create list from list where values only increase by 1

I have the code below that gets the maximum value from a list. It then compares it to the maximum value of the remaining values in the list, and if it is more than 1 higher than the next greatest value, it replaces the original list maximum with 1 higher than the next greatest value. I would like the code to search the entire list and make sure that any value in the list is at most 1 larger than any other value in the list. I know this ins’t the best worded explanation, I hope the example lists below make what I’m trying to accomplish clearer.
for example I don’t want to get a final list like:
[0,2,0,3]
I would want the final list to be
[0,1,0,2]
input:
empt=[0,2,0,0]
Code:
nwEmpt=[i for i in empt if i !=max(empt)]
nwEmpt2=[]
for i in range(0,len(empt)):
if (empt[i]==max(empt))&(max(empt)>(max(nwEmpt)+1)):
nwEmpt2.append((max(nwEmpt)+1))
elif (empt[i]==max(empt))&(max(empt)==(max(nwEmpt)+1)):
nwEmpt2.append(max(empt))
else:
nwEmpt2.append(empt[i])
output:
nwEmpt2
[0,1,0,0]
min_value = min(empt)
empt_set = set(empt)
for i in empt:
nwEmpt.append(min_value + len(list(filter(lambda x: x < i, empt_set))))
This gives e.g. for input empt = [8, 10, 6, 4, 4] output nwEmpt = [6, 7, 5, 4, 4].
It works by mapping each element to (the minimum value) + (the number of distinct values smaller than element).

What is the empty dictionary used for in the code?

I'm doing practice problems in python on Leetcode (still learning). This is the problem:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
my code is
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dict = {}
for counter, i in enumerate(nums):
a = target- i
if a in dict:
return (dict[a], counter)
dict[i] = counter
It runs fine and passes all the tests however I found a common reason this works is for the dict = {}
What is the reason for this dictionary and how does this code recognize cases for (3,3) target = 6 where there are duplicates and index matters. A basic run down of why the code works would be great!
The dictionary stores as keys the numbers in the list with their index as a value.
For example:
[2, 7, 11, 15] -> {'2':0, '7':1, '11':2, '15':3}
There is never a duplicate inserted, if the same number appears twice, the index will be replaced with the new index where it appears.
In the case of duplicate, it is important to test all value on the first list, and to store index on a separated dict in order to be sur that you will never test in dictionnary the actually tested value.
By using a dictionnary in order to find the index of the right number, you can't store duplicate.
Since in dictionnary you can't have 2 values with the same key, if duplicate, you just change the old index with the new one.
For example, if dict == {'3': 0, '2':1} and the tested value is 2, the dict == {'3': 0, '2':2}.
And if the target is reach by duplicate number (2+2 for target 4 for example), nothing is stored cause of the return in the if a in dict: return (dict[a], counter)

python3 functional programming: Accumulating items from a different list into an initial value

I have some code that performs the following operation, however I was wondering if there was a more efficient and understandable way to do this. I am thinking that there might be something in itertools or such that might be designed to perform this type of operation.
So I have a list of integers the represents changes in the number of items from one period to the next.
x = [0, 1, 2, 1, 3, 1, 1]
Then I need a function to create a second list that accumulates the total number of items from one period to the next. This is like an accumulate function, but with elements from another list instead of from the same list.
So I can start off with an initial value y = 3.
The first value in the list y = [3]. The I would take the second
element in x and add it to the list, so that means 3+1 = 4. Note that I take the second element because we already know the first element of y. So the updated value of y is [3, 4]. Then the next iteration is 4+2 = 6. And so forth.
The code that I have looks like this:
def func():
x = [0, 1, 2, 1, 3, 1, 1]
y = [3]
for k,v in enumerate(x):
y.append(y[i] + x[i])
return y
Any ideas?
If I understand you correctly, you do what what itertools.accumulate does, but you want to add an initial value too. You can do that pretty easily in a couple ways.
The easiest might be to simply write a list comprehension around the accumulate call, adding the initial value to each output item:
y = [3 + val for val in itertools.accumulate(x)]
Another option would be to prefix the x list with the initial value, then skip it when accumulate includes it as the first value in the output:
acc = itertools.accumulate([3] + x)
next(acc) # discard the extra 3 at the start of the output.
y = list(acc)
Two things I think that need to be fixed:
1st the condition for the for loop. I'm not sure where you are getting the k,v from, maybe you got an example using zip (which allows you to iterate through 2 lists at once), but in any case, you want to iterate through lists x and y using their index, one approach is:
for i in range(len(x)):
2nd, using the first append as an example, since you are adding the 2nd element (index 1) of x to the 1st element (index 0) of y, you want to use a staggered approach with your indices. This will also lead to revising the for loop condition above (I'm trying to go through this step by step) since the first element of x (0) will not be getting used:
for i in range(1, len(x)):
That change will keep you from getting an index out of range error. Next for the staggered add:
for i in range(1, len(x)):
y.append(y[i-1] + x[i])
return y
So going back to the first append example. The for loop starts at index 1 where x = 1, and y has no value. To create a value for y[1] you append the sum of y at index 0 to x at index 1 giving you 4. The loop continues until you've exhausted the values in x, returning accumulated values in list y.

How to get list of indices for elements whose value is the maximum in that list

Suppose I have a list l=[3,4,4,2,1,4,6]
I would like to obtain a subset of this list containing the indices of elements whose value is max(l).
In this case, list of indices will be [1,2,5].
I am using this approach to solve a problem where, a list of numbers are provided, for example
l=[1,2,3,4,3,2,2,3,4,5,6,7,5,4,3,2,2,3,4,3,4,5,6,7]
I need to identify the max occurence of an element, however in case more than 1 element appears the same number of times,
I need to choose the element which is greater in magnitude,
suppose I apply a counter on l and get {1:5,2:5,3:4...}, I have to choose '2' instead of '1'.
Please suggest how to solve this
Edit-
The problem begins like this,
1) a list is provided as an input
l=[1 4 4 4 5 3]
2)I run a Counter on this to obtain the counts of each unique element
3)I need to obtain the key whose value is maximum
4)Suppose the Counter object contains multiple entries whose value is maximum,
as in Counter{1:4,2:4,3:4,5:1}
I have to choose 3 as the key whose value is 4.
5)So far, I have been able to get the Counter object, I have seperated key/value lists using k=counter.keys();v=counter.values()
6)I want to get the indices whose values are max in v
If I run v.index(max(v)), I get the first index whose value matches max value, but I want to obtain the list of indices whose value is max, so that I can obtain corresponding list of keys and obtain max key in that list.
With long lists, using NumPy or any other linear algebra would be helpful, otherwise you can simply use either
l.index(max(l))
or
max(range(len(l)),key=l)
These however return only one of the many argmax's.
So for your problem, you can choose to reverse the array, since you want the maximum that appears later as :
len(l)-l[::-1].index(max(l))-1
If I understood correctly, the following should do what you want.
from collections import Counter
def get_largest_most_freq(lst):
c = Counter(lst)
# get the largest frequency
freq = max(c.values())
# get list of all the values that occur _max times
items = [k for k, v in c.items() if v == freq]
# return largest most frequent item
return max(items)
def get_indexes_of_most_freq(lst):
_max = get_largest_most_freq(lst)
# get list of all indexes that have a value matching _max
return [i for i, v in enumerate(lst) if v == _max]
>>> lst = [3,4,4,2,1,4,6]
>>> get_largest_most_freq(lst)
4
>>> get_indexes_of_most_freq(lst)
[1, 2, 5]
>>> lst = [1,2,3,4,3,2,2,3,4,5,6,7,5,4,3,2,2,3,4,3,4,5,6,7]
>>> get_largest_most_freq(lst)
3
>>> get_indexes_of_most_freq(lst)
[2, 4, 7, 14, 17, 19]

Resources