Python list issue - python

I need some hints or an example, how can i localize in a list a the list b, then replace it with list c.
a=[1,3,6,2,6,7,3,4,5,6,6,7,8]
input the b list (this is the sublist the program searches for in list a).
b=[6,7]
when found return me the indexes were the sublist has been found and replace it each time with c=[0,0], so the result will be
[1,3,6,2,0,0,3,4,5,6,0,0,8]

Here's a more efficient approach than my first, using list-slicing:
>>> for i in xrange(len(a) - len(b) + 1):
... if a[i:i+len(b)] == b:
... a[i:i+len(b)] = c
...
>>> a
[1, 3, 6, 2, 0, 0, 3, 4, 5, 6, 0, 0, 8]
First attempt, for posterity....
If you don't need the intermediate indices, here's one approach, using string functions and taking a functional approach, not modifying your list in-place.
>>> a_as_str = ','.join(str(i) for i in a)
>>> print a_as_str
1,3,6,2,6,7,3,4,5,6,6,7,8
>>> b_as_str = ','.join(str(i) for i in b)
>>> b_as_str
'6,7'
>>> c_as_str = ','.join(str(i) for i in c)
>>> c_as_str
'0,0'
>>> replaced = a_as_str.replace(b_as_str, c_as_str)
>>> replaced
'1,3,6,2,0,0,3,4,5,6,0,0,8'
>>> [int(i) for i in replaced.split(',')]
[1, 3, 6, 2, 0, 0, 3, 4, 5, 6, 0, 0, 8]
This can be refactored as:
>>> def as_str(l):
... return ','.join(str(i) for i in l)
...
>>> def as_list_of_ints(s):
... return [int(i) for i in s.split(',')]
...
>>> as_list_of_ints(as_str(a).replace(as_str(b), as_str(c)))
[1, 3, 6, 2, 0, 0, 3, 4, 5, 6, 0, 0, 8]

you can do something similar to (written in python 3.2, use xrange in python 2.x):
for i in range(0, len(a)):
if a[i:i+len(b)] == b:
a[i:i+len(b)] = c
this will account for lists of all sizes.
This assumes list b == list c I don't know if that is what you want however, please state if it is not.
Output for lists:
a = [1,2,3,4,5,6,7,8,9,0]
b = [1,2]
c = [0,0]
Output:
[0, 0, 3, 4, 5, 6, 7, 8, 9, 0]

I give you an example
li=[1,3,6,2,6,7,3,4,5,6,6,7,8]
for i in range(len(li)):
if li[i:i + 2] == [3, 4]:
li[i:i + 2] = [0, 0]
I think that this code should work. If you want a more robust script I suggest you to check the occurrences of a substring in the original list an edit a copy (to avoid side-effect behaviors).

It is important also to consider what happens when the given pattern is created by the substitution.
I think this function should treat all cases as intended:
def replace(a, b, c):
ii = 0
while ii <= (len(a) - len(b) + 1):
print(ii)
if a[ii:ii+len(b)] == b:
a[ii:ii+len(b)] = c
ii += len(b)
else:
ii += 1
return a
The output using the original example:
[1, 3, 6, 2, 0, 0, 3, 4, 5, 6, 0, 0, 8]
Here is an example where the substitution creates the search pattern:
a = [1,1,1,1,1,1,1,1,1,6,6,7,7,1]
b = [6,7]
c = [0,6]
Output is as expected:
[1, 1, 1, 1, 1, 1, 1, 1, 1, 6, 0, 6, 7, 1]
Any ideas on how to do this a bit more concisely?

Related

group elements in list by pairwise criterion in python

I am implementing a local optimization that fuses objects together. In the simplest form, given a list:
[0, 3, 5, 8, 1, 2, 9, 0, 3, 5]
I would like to group into:
[[0, 3], 5, 8, 1, 2, 9, [0, 3], 5]
which is based on a provided criterion:
def is_group(a, b):
return a == 0 and b == 3
My current solution seems a bit convoluted, and am looking for most pythonic approach:
def pairwise(iterable):
for (a, b) in zip(iterable, iterable[1:]):
yield (a, b)
yield (iterable[-1], None) # handle edge case
def fuse(ops):
ops_new = []
skip_next = False
for (a, b) in pairwise(ops):
if is_group(a, b):
ops_new.append([a, b])
skip_next = True
elif skip_next:
skip_next = False
elif:
ops_new.append(a)
I've looked at groupby, which is the closest but aren't quite sure how to make it work since here the criterion depends on pairwise arguments.
Edit: Another way to ask the question is I am basically trying to do pattern search and replace with lists (e.g. regex for lists).
Custom isolate_group function:
def isolate_group(pair, l):
result = []
idx_skip = -1
for i in range(len(l)):
if i == idx_skip:
continue
if l[i:i+2] == pair:
result.append(l[i:i+2])
idx_skip = i+1
else:
result.append(l[i])
return result
Test 1:
print(isolate_group([0,3], [0, 3, 5, 8, 1, 2, 9, 0, 3, 5]))
The output:
[[0, 3], 5, 8, 1, 2, 9, [0, 3], 5]
Test 2:
print(isolate_group([0,3], [0, 3, 5, 8, 0, 3, 9, 5, 0, 3]))
The output:
[[0, 3], 5, 8, [0, 3], 9, 5, [0, 3]]

Add element into list at even indexes [duplicate]

This question already has answers here:
python: most elegant way to intersperse a list with an element
(15 answers)
Closed 6 years ago.
I have a list like
[1, 2, 3, 4, 5]
and I want to add zeroes at odd indexes:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
My first thought was to create a list with zeroes and replace them with the values from the original list.
listOfZeros = [0] * (2*len(list)-1)
j = 0
for i in range(0, len(listOfZeros)):
if (i%2 == 0):
listOfZeros[i] = h_temp[j]
j += 1
This actually works, but I do dislike for loops and adding another counter j. Isn't there a better way by using slicing?
You can use insert(). Looking at your output, assuming you are not counting index 0 as even.
a = [1,2,3,4,5]
for x in range(len(a)):
a.insert(2*x+1, 0)
one way is by using zip:
a = [1, 2, 3, 4, 5]
d = [x for t in zip (a, [0] * len(a)) for x in t][:-1]
When you use zip, you create list of tuples.
a = [1,2,3,4,5]
b = [0,0,0,0,0]
c = zip(a,b)
#zip (a,b) creates [(1,0),(2,0),(3,0),(4,0),(5,0)]
Then you loop over the set of tuples to arrange them into list:
d = [x for t in c for x in t] #creates [1,0,2,0,3,0,4,0,5,0]
and cut the last element (since you end with 5)
[x for t in c for x in t][:-1] #take out the last 0
#resulting in [1,0,2,0,3,0,4,0,5]
then you are done.
You can do it with a generator:
def zero_on_odd(mylist):
for i in mylist:
yield i
yield 0
a = [1, 2, 3]
with_zeros = list(zero_on_odd(a))[:-1]
If you want to go functional...
from itertools import chain, repeat
_list = [1,2,3,4,5]
list(chain(*zip(_list, repeat(0))))[:-1]
# [1, 0, 2, 0, 3, 0, 4, 0, 5]
If you want to be silly...
[int(i) for i in '0'.join(str(i) for i in _list)]
# still [1, 0, 2, 0, 3, 0, 4, 0, 5]
Or, if you want to be functional AND silly...
map(int, '0'.join(map(str, _list)))
# really, it's still [1, 0, 2, 0, 3, 0, 4, 0, 5]
# except in Python 3.X, there it's a map object...
But, you should probably opt for one of the custom generator solutions.
For the fun of it, here is an itertools solution:
from itertools import islice, chain
data = [1,2,3,4,5]
print list(islice(chain.from_iterable((x, 0) for x in data), 0, 2 * len(data)-1))
Giving:
[1, 0, 2, 0, 3, 0, 4, 0, 5]
Another zip way:
>>> li
[1, 2, 3, 4, 5]
>>> [e for t in zip(li,[0]*(len(li)-1)) for e in t]+[li[-1]]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
You can also use range and slice assignment:
>>> li=[1,2,3,4,5]
>>> for i in range(1,len(li)+len(li)-1, 2): li[i:i]=[0]
...
>>> li
[1, 0, 2, 0, 3, 0, 4, 0, 5]
And, a list comprehension:
>>> [li[i/2] if not i%2 else 0 for i in range(len(li)*2-1)]
[1, 0, 2, 0, 3, 0, 4, 0, 5]
A hacky way:
>>> ls1 = [1, 2, 3, 4, 5]
>>> ls2 = []
>>> list(ls2.extend([n, 0]) for n in ls1)
[None, None, None, None, None]
>>> ls2
[1, 0, 2, 0, 3, 0, 4, 0, 5, 0]

Interleave list with fixed element

I know that I can interleave two python lists with:
[elem for pair in zip(*lists) for elem in pair]
Now I need to interleave a list with a fixed element like:
list = [1, 2, 3, 4]
# 🐍 python magic 🐍
output = [1, 0, 2, 0, 3, 0, 4]
One really straightforward solution is:
[elem for x in list for elem in (x, 0)][:-1]
You can try the following itertools magic:
>>> from itertools import repeat, chain, izip
>>> l = [1, 2, 3, 4]
>>> list(chain.from_iterable(izip(l[:-1], repeat(0)))) + l[-1:]
[1, 0, 2, 0, 3, 0, 4]
from itertools import izip, repeat
start = [1, 2, 3, 4]
print [i for j in izip(start, repeat(0)) for i in j][:-1]
Python's sum function can be used on arbitrary datatypes that support addition by setting the start parameter appropriately. (see docs)
input = [1, 2, 3, 4]
fixed = 0
output = sum([[elem, fixed] for elem in input], [])[:-1] # to drop the last `fixed`
Or if you don't like the idea of using the addition operator with lists:
input = [1, 2, 3, 4]
fixed = 0
output = []
for elem in input:
output.extend([elem, fixed])
output = output[:-1]
>>> lst = [1, 2, 3, 4]
>>> newlst = [0]*((len(lst) * 2) - 1)
>>> newlst[::2] = lst
>>> newlst
[1, 0, 2, 0, 3, 0, 4]
It may not be a one-liner, but it works. Furthermore, my time tests seem to show that it's the fastest solution so far. In function form, this is:
def interzero(lst):
newlst = [0]*((len(lst) * 2) - 1)
newlst[::2] = lst
return newlst
You could use the reduce function of functools.
>>> from functools import reduce
>>> reduce(lambda x, y: x + [y, 0], [1,2,3,4], [])[:-1]
[1, 0, 2, 0, 3, 0, 4]
>>> from itertools import chain
>>> lst = [1, 2, 3, 4]
>>> list(chain(*zip(lst, [0]*(len(lst)-1)))) + [lst[-1]]
[1, 0, 2, 0, 3, 0, 4]

Merge two list with some order rule in python

I don't know how to give an accurate title, but here's the problem.
The problem:
I want to give a ranking list (imagine some top list) with some position preserved already.
Say I got 7 slots [1, 2, 3, 4, 5, 6, 7, 8] and some has already preserved postion 1, 3, 4, 7, 9. (As we only have 8 slots, the perserved postion 9 will means the last slot.)
Then I have 2, 5, 6 slots left, which I have to fill them with other objects.
The simplified question:
I have two list:
>>> a = [1, 3, 4, 7, 9]
>>> b = [object_x, object_y, object_z]
And I want to merge them to this:
>>> c = [1, object_x, 3, 4, object_y, object_z, 7, 9]
(We can take the 'object_x' here as 0.)
That's it, just want to see if there is an elegant way to implement this.
(Edit whole question based on the comments. Thank you guys very much.)
You could use a generator:
def merge(a, b):
b_clone = b[:]
for n in range(min(a), max(a) + 1):
if n in a:
yield n
elif b_clone:
yield b_clone.pop(0)
I believe this covers the edge cases, I do, however, agree with others that it seems there must be a better way of doing this. It might be worth explaining the context of what you are trying to do. There is probably a way to do it without all of this.
def merge(a, b):
b = list(b)
a = iter(a)
current = 1
for item in a:
while item != current:
if b:
yield b.pop(0)
else:
yield item
yield from a # <3.3 use `for item in a: yield item` instead.
return
current += 1
yield item
current += 1
Which appears to work as per your spec:
>>> print(list(merge([1, 3, 4, 7, 9], [0, 0, 0])))
[1, 0, 3, 4, 0, 0, 7, 9]
>>> print(list(merge([2, 4, 5], [1, 3])))
[1, 2, 3, 4, 5]
It's also unclear what should happen given extra elements in b - this ignores them, but adding yield from b (or, <3.3 for item in b: yield item) to the end would give them as the final elements.
This should also work:
>>>a = [2, 3, 5, 6, 7, 9]
>>>b = [0, 0, 0]
>>>length = len(a)
>>>i = 0
>>>while (i < length):
if a[i] != i+1:
a.insert(i, b.pop(0))
length += 1
i += 1
>>>print(a)
[0, 2, 3, 0, 5, 6, 7, 0, 9]
Thanks all, and I get this solution inspired by #jurgenreza
>>> a = [1, 3, 4, 7, 9]
>>> b = [0, 0, 0]
>>> for i in a:
b.insert(i - 1, i)
>>> print b
[1, 0, 3, 4, 0, 0, 7, 9]
You would possibly be better off avoiding having to do this merging in the first place. If you know in advance how many possible ranks there are, you can start your list off as containing that many Nones, and every time you occupy a space,set that using list item assignment rather than appending. Then your final merge is as simple as:
def merge(a, b):
b = iter(b)
for i,x in a:
if x is None:
a[i] = next(b)
If you were so inclined, you could put this whoe data structure into a class, which would also allow you to, for example, check when you're trying to overwrite an occupied position (if that would be an error):
class Ranks:
def __init__(self, size):
self._list = [None] * size
def __getitem__(self, position):
return self._list[position]
def __setitem__(self, position, val):
if self._list[position] is None:
raise ValueError('attempting to clobber existing rank data')
self._list[position] = val

Add to integers in a list

I have a list of integers and I was wondering if it would be possible to add to individual integers in this list.
You can append to the end of a list:
foo = [1, 2, 3, 4, 5]
foo.append(4)
foo.append([8,7])
print(foo) # [1, 2, 3, 4, 5, 4, [8, 7]]
You can edit items in the list like this:
foo = [1, 2, 3, 4, 5]
foo[3] = foo[3] + 4
print(foo) # [1, 2, 3, 8, 5]
Insert integers into the middle of a list:
x = [2, 5, 10]
x.insert(2, 77)
print(x) # [2, 5, 77, 10]
Here is an example where the things to add come from a dictionary
>>> L = [0, 0, 0, 0]
>>> things_to_add = ({'idx':1, 'amount': 1}, {'idx': 2, 'amount': 1})
>>> for item in things_to_add:
... L[item['idx']] += item['amount']
...
>>> L
[0, 1, 1, 0]
Here is an example adding elements from another list
>>> L = [0, 0, 0, 0]
>>> things_to_add = [0, 1, 1, 0]
>>> for idx, amount in enumerate(things_to_add):
... L[idx] += amount
...
>>> L
[0, 1, 1, 0]
You could also achieve the above with a list comprehension and zip
L[:] = [sum(i) for i in zip(L, things_to_add)]
Here is an example adding from a list of tuples
>>> things_to_add = [(1, 1), (2, 1)]
>>> for idx, amount in things_to_add:
... L[idx] += amount
...
>>> L
[0, 1, 1, 0]
fooList = [1,3,348,2]
fooList.append(3)
fooList.append(2734)
print(fooList) # [1,3,348,2,3,2734]
If you try appending the number like, say
listName.append(4) , this will append 4 at last.
But if you are trying to take <int> and then append it as, num = 4 followed by listName.append(num), this will give you an error as 'num' is of <int> type and listName is of type <list>. So do type cast int(num) before appending it.
Yes, it is possible since lists are mutable.
Look at the built-in enumerate() function to get an idea how to iterate over the list and find each entry's index (which you can then use to assign to the specific list item).

Categories