I was instructed to prevent this from happening in a Python program but frankly I have no idea how this is even possible. Can someone give an example of how you can slice a list and insert something into it to make it bigger? Thanks
>>> a = [1,2,3]
>>> a[:0] = [4]
>>> a
[4, 1, 2, 3]
a[:0] is the "slice of list a beginning before any elements and ending before index 0", which is initially an empty slice (since there are no elements in the original list before index 0). If you set it to be a non-empty list, that will expand the original list with those elements. You could also do the same anywhere else in the list by specifying a zero-width slice (or a non-zero width slice, if you want to also replace existing elements):
>>> a[1:1] = [6,7]
>>> a
[4, 6, 7, 1, 2, 3]
To prevent this from happening you can subclass the builtin list and then over-ride these methods for details refer here
Related
I have a list that contains many elements, where each element represents an input file, that I want to dynamically subset using the values contained within another list. For example, I have some code that dynamically generates lists that I want to use to define the sub-samples such as
[0, 1, 2, 3]
and
[1, 2, 3, 4]
But I want to use the start and end elements of each of these lists to define an slice range to be applied to another list. In other words, I want the two above lists to be converted into slices that look like this
[0:3]
and [1:4]
But I don't know how to do this, and to be honest I'm not even sure the correct terminology to use to search for this. I have tried searching stack overflow for 'dynamically generate slices from lists' or even 'dynamically generate data slice' (an variants that I can think of along those lines) without any success.
Here are a few more details:
thislist = ['2019/12/26/fjjd', '2019/12/26/defg', '2020/01/09/qpfd', '2020/01/09/tosf', '2020/01/16/zpqr', '2020/01/15/zpqr', '2020/01/15/juwi']
where someIndexSlice is
[0:3]
and generated from a list that looks like this
[0,1,2,3]
thislist[someIndexSlice] = ['2019/12/26/fjjd', '2019/12/26/defg', '2020/01/09/qpfd', '2020/01/09/tosf']
So my questions are:
How can I accomplish this?
What sort of terminology should I use to describe what I am trying to accomplish?
Thanks
You can use the built-in slice function:
>>> lst = [0, 1, 2, 3]
>>> as_slice = slice(lst[0], lst[-1], lst[1] - lst[0])
>>> as_slice
slice(0, 3, 1) # which is same as [0:3]
And then to check if it works correctly:
>>> test = [1, 5, 3, 7, 8]
>>> test[as_slice]
[1, 5, 3]
>>> test[0:3]
[1, 5, 3]
NOTE:
This implementation assumed your lists are equidistant, and sorted.
Consider the following simple python code
>>> L = range(3)
>>> L
[0, 1, 2]
We can take slices of this array as follows:
>>> L[1:3]
[1, 2]
Is there any way to wrap around the above array by shifting to the left
[1, 2, 0]
by simply using slice operations?
Rotate left n elements (or right for negative n):
L = L[n:] + L[:n]
Note that collections.deque has support for rotations. It might be better to use that instead of lists.
Left:
L[:1], L[1:] = L[-1:], L[:-1]
Right:
L[-1:], L[:-1] = L[:1], L[1:]
To my mind, there's no way, unless you agree to cut and concatenate lists as shown above.
To make the wrapping you describe you need to alter both starting and finishing index.
A positive starting index cuts away some of initial items.
A negative starting index gives you some of the tail items, cutting initial items again.
A positive finishing index cuts away some of the tail items.
A negative finishing index gives you some of the initial items, cutting tail items again.
No combination of these can provide the wrapping point where tail items are followed by initial items. So the entire thing can't be created.
Numerous workarounds exist. See answers above, see also itertools.islice and .chain for a no-copy sequential approach if sequential access is what you need (e.g. in a loop).
If you are not overly attached to the exact slicing syntax, you can write a function that produces the desired output including the wrapping behavior.
E.g., like this:
def wrapping_slice(lst, *args):
return [lst[i%len(lst)] for i in range(*args)]
Example output:
>>> L = range(3)
>>> wrapping_slice(L, 1, 4)
[1, 2, 0]
>>> wrapping_slice(L, -1, 4)
[2, 0, 1, 2, 0]
>>> wrapping_slice(L, -1, 4, 2)
[2, 1, 0]
Caveat: You can't use this on the left-hand side of a slice assignment.
I know the difference in a[:] and a in assignment to a variable and also the special case of slice assignment.
Suppose,
a=[1,2,3,4,5]
What is the difference between the following two statements?
b=a[:]+[6,7,8,9,10] #1
b=a+[6,7,8,9,10] #2
In both cases, both a and b have the same values at the end.
I have referred the following links -
When and why to use [:] in python
Understanding slice notation
Python why would you use [:] over =
They haven't mentioned their difference in an expression as such.
a[:] grabs a full slice of the list – in this context, it has no difference in effect since you're assigning to a new list (though it does copy the list, so it's slower at scale).
# create the list.
>>> a = [1, 2, 3, 4, 5]
# see its address
>>> id(a)
4349194440
# see the (different) address of a copy
>>> id(a[:])
4350338120
# reassign the entire list using slice syntax
>>> a[:] = [5, 6, 7]
>>> a
[5, 6, 7]
# still the same first ID though
>>> id(a)
4349194440
>>>
In python list slicing a[:] and a has only difference in their id's because a[:] is making an exact copy of a in another address location.
Also considering python immutable string slicing a[:] and a have no difference.Both points to same address location.
a=[1,2,3,4,5]
b=a[:]+[6,7,8,9,10] #1
b=a+[6,7,8,9,10] #2
Case-1 a[:] , means you are slicing the sequence and a sequence can be anything like string, list etc. Basically this is read as a[start:end:steps],where start end are our indexing values AND steps are number of jumps. If we do not provide any values then by default start = 0 AND end = last element of sequence AND steps = 1. So in your case, you are simply taking the whole elements of list a.
Case-2 a , It simply means the whole a
Conclusion:- With the help of a[:] you can get the desired elements.
Examples-->>
a = [1,2,3,4]
a[1:4]
>> [1,2,3]
a[::2]
>> [1,3]
I hope it may help you.
I have a list:
input = ['a','b','c','a','b','d','e','d','g','g']
I want index of all elements except duplicate in a list.
output = [0,1,2,5,6,8]
You should iterate over the enumerated list and add each element to a set of "seen" elements and add the index to the output list if the element hasn't already been seen (is not in the "seen" set).
Oh, the name input overrides the built-in input() function, so I renamed it input_list.
output = []
seen = set()
for i,e in enumerate(input_list):
if e not in seen:
output.append(i)
seen.add(e)
which gives output as [0, 1, 2, 5, 6, 8].
why use a set?
You could be thinking, why use a set when you could do something like:
[i for i,e in enumerate(input_list) if input_list.index(e) == i]
which would work because .index returns you the index of the first element in a list with that value, so if you check the index of an element against this, you can assert that it is the first occurrence of that element and filter out those elements which aren't the first occurrences.
However, this is not as efficient as using a set, because list.index requires Python to iterate over the list until it finds the element (or doesn't). This operation is O(n) complexity and since we are calling it for every element in input_list, the whole solution would be O(n^2).
On the other hand, using a set, as in the first solution, yields an O(n) solution, because checking if an element is in a set is complexity O(1) (average case). This is due to how sets are implemented (they are like lists, but each element is stored at the index of its hash so you can just compute the hash of an element and see if there is an element there to check membership rather than iterating over it - note that this is a vague oversimplification but is the idea of them).
Thus, since each check for membership is O(1), and we do this for each element, we get an O(n) solution which is much better than an O(n^2) solution.
You could do a something like this, checking for counts (although this is computation-heavy):
indexes = []
for i, x in enumerate(inputlist):
if (inputlist.count(x) == 1
and x not in inputlist[:i]):
indexes.append(i)
This checks for the following:
if the item appears only once. If so, continue...
if the item hasn't appeared before in the list up till now. If so, add to the results list
In case you don't mind indexes of the last occurrences of duplicates instead and are using Python 3.6+, here's an alternative solution:
list(dict(map(reversed, enumerate(input))).values())
This returns:
[3, 4, 2, 7, 6, 9]
Here is a one-liner using zip and reversed
>>> input = ['a','b','c','a','b','d','e','d','g','g']
>>> sorted(dict(zip(reversed(input), range(len(input)-1, -1, -1))).values())
[0, 1, 2, 5, 6, 8]
This question is missing a pandas solution. 😉
>>> import pandas as pd
>>> inp = ['a','b','c','a','b','d','e','d','g','g']
>>>
>>> pd.DataFrame(list(enumerate(inp))).groupby(1).first()[0].tolist()
[0, 1, 2, 5, 6, 8]
Yet another version, using a side effect in a list comprehension.
>>> xs=['a','b','c','a','b','d','e','d','g','g']
>>> seen = set()
>>> [i for i, v in enumerate(xs) if v not in seen and not seen.add(v)]
[0, 1, 2, 5, 6, 8]
The list comprehension filters indices of values that have not been seen already.
The trick is that not seen.add(v) is always true because seen.add(v) returns None.
Because of short circuit evaluation, seen.add(v) is performed if and only if v is not in seen, adding new values to seen on the fly.
At the end, seen contains all the values of the input list.
>>> seen
{'a', 'c', 'g', 'b', 'd', 'e'}
Note: it is usually a bad idea to use side effects in list comprehension,
but you might see this trick sometimes.
I am relatively new to python and I am still trying to learn the basics of the language. I stumbled upon a question which asks you to rearrange the list by modifying the original. What you are supposed to do is move all the even index values to the front (in reverse order) followed by the odd index values.
Example:
l = [0, 1, 2, 3, 4, 5, 6]
l = [6, 4, 2, 0, 1, 3, 5]
My initial approach was to just use the following:
l = l[::-2] + l[1::2]
However, apparently this is considered 'creating a new list' rather than looping through the original list to modify it.
As such, I was hoping to get some ideas or hints as to how I should approach this particular question. I know that I can use a for loop or a while loop to cycle through the elements / index, but I don't know how to do a swap or anything else for that matter.
You can do it by assigning to a list slice instead of a variable:
l[:] = l[::2][::-1] + l[1::2]
Your expression for the reversed even elements was also wrong. Use l[::2] to get all the even numbers, then reverse that with [::-1].
This is effectively equivalent to:
templ = l[::2][::-1] + l[1::2]
for i in range(len(l)):
l[i] = templ[i]
The for loop modifies the original list in place.