1

I first want to note that my question is different from what's in this link: finding and replacing elements in a list (python)

What I want to ask is whether there is some known API or conventional way to achieve such a functionality (If it's not clear, a function/method like my imaginary list_replace() is what I'm looking for):

>>> list = [1, 2, 3]
>>> list_replace(list, 3, [3, 4, 5])
>>> list
[1, 2, 3, 4, 5]

An API with limitation of number of replacements will be better:

>>> list = [1, 2, 3, 3, 3]
>>> list_replace(list, 3, [8, 8], 2)
>>> list
[1, 2, 8, 8, 8, 8, 3]

And another optional improvement is that the input to replace will be a list itself, instead of a single value:

>>> list = [1, 2, 3, 3, 3]
>>> list_replace(list, [2, 3], [8, 8], 2)
>>> list
[1, 8, 8, 3, 3]

Is there any API that looks at least similar and performs these operations, or should I write it myself?

SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
  • I guess the simplest implement is: convert list to str, replace, and then convert back... – eph Dec 09 '15 at 09:35
  • But what if your list is `['cat', 'cat and dog']` and you only want to replace `cat` ? The conversion to list will also replace the `cat` withing the `cat and dog`. Also, how do you plan to implement the inverse function (string back to list) ? – SomethingSomething Dec 09 '15 at 09:36
  • I know what you mean. Just mentioned for list of ints. – eph Dec 09 '15 at 09:40
  • The list replacement is underspecified. What if some elements in the original list are lists? – Koterpillar Dec 09 '15 at 10:01
  • So you can call the function with list of lists as source and target list members – SomethingSomething Dec 09 '15 at 10:08

5 Answers5

1

Try;

def list_replace(ls, val, l_insert, num = 1):
    l_insert_len = len(l_insert)
    indx = 0
    for i in range(num):
        indx = ls.index(val, indx) #it throw value error if it cannot find an index
        ls = ls[:indx] + l_insert + ls[(indx + 1):]
        indx += l_insert_len
    return ls

This function works for both first and second case;
It wont work with your third requirement

Demo

>>> list = [1, 2, 3]
>>> list_replace(list, 3, [3, 4, 5])
[1, 2, 3, 4, 5]
>>> list = [1, 2, 3, 3, 3]
>>> list_replace(list, 3, [8, 8], 2)
[1, 2, 8, 8, 8, 8, 3]

Note It returns a new list; The list passed in will not change.

Praveen
  • 8,945
  • 4
  • 31
  • 49
1

how about this, it work for the 3 requirements

def list_replace(origen,elem,new,cantidad=None):
    n=0
    resul=list()
    len_elem=0
    if isinstance(elem,list):
        len_elem=len(elem)
    for i,x in enumerate(origen):
        if x==elem or elem==origen[i:i+len_elem]:
            if cantidad and n<cantidad:
                resul.extend(new)
                n+=1
                continue
            elif not cantidad:
                resul.extend(new)
                continue
        resul.append(x)
    return resul



>>>list_replace([1,2,3,4,5,3,5,33,23,3],3,[42,42])
[1, 2, 42, 42, 4, 5, 42, 42, 5, 33, 23, 42, 42]
>>>list_replace([1,2,3,4,5,3,5,33,23,3],3,[42,42],2)
[1, 2, 42, 42, 4, 5, 42, 42, 5, 33, 23, 3]
>>>list_replace([1,2,3,4,5,3,5,33,23,3],[33,23],[42,42,42],2)
[1, 2, 3, 4, 5, 3, 5, 42, 42, 42, 23, 3]
Copperfield
  • 8,131
  • 3
  • 23
  • 29
0

Given this isn't hard to write, and not a very common use case, I don't think it will be in the standard library. What would it be named, replace_and_flatten? It's quite hard to explain what that does, and justify the inclusion.

Explicit is also better than implicit, so...

def replace_and_flatten(lst, searched_item, new_list):
    def _replace():
        for item in lst:
            if item == searched_item:
                yield from new_list  # element matches, yield all the elements of the new list instead
            else:
                yield item  # element doesn't match, yield it as is

    return list(_replace())  # convert the iterable back to a list
Koterpillar
  • 7,883
  • 2
  • 25
  • 41
0

I developed my own function, you are welcome to use and to review it.

Note that in contradiction to the examples in the question - my function creates and returns a new list. It does not modify the provided list.

Working examples:

list = [1, 2, 3]
l2 = list_replace(list, [3], [3, 4, 5])
print('Changed: {0}'.format(l2))
print('Original: {0}'.format(list))

list = [1, 2, 3, 3, 3]
l2 = list_replace(list, [3], [8, 8], 2)
print('Changed: {0}'.format(l2))
print('Original: {0}'.format(list))

list = [1, 2, 3, 3, 3]
l2 = list_replace(list, [2, 3], [8, 8], 2)
print('Changed: {0}'.format(l2))
print('Original: {0}'.format(list))

I always print also the original list, so you can see that it is not modified:

Changed: [1, 2, 3, 4, 5]
Original: [1, 2, 3]
Changed: [1, 2, 8, 8, 8, 8, 3]
Original: [1, 2, 3, 3, 3]
Changed: [1, 8, 8, 3, 3]
Original: [1, 2, 3, 3, 3]

Now, the code (tested with Python 2.7 and with Python 3.4):

def list_replace(lst, source_sequence, target_sequence, limit=0):
    if limit < 0:
        raise Exception('A negative replacement limit is not supported')
    source_sequence_len = len(source_sequence)
    target_sequence_len = len(target_sequence)
    original_list_len = len(lst)
    if source_sequence_len > original_list_len:
        return list(lst)
    new_list = []
    i = 0
    replace_counter = 0
    while i < original_list_len:
        suffix_is_long_enough = source_sequence_len <= (original_list_len - i)
        limit_is_satisfied = (limit == 0 or replace_counter < limit)
        if suffix_is_long_enough and limit_is_satisfied:
            if lst[i:i + source_sequence_len] == source_sequence:
                new_list.extend(target_sequence)
                i += source_sequence_len
                replace_counter += 1
                continue
        new_list.append(lst[i])
        i += 1      
    return new_list
SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
0

I developed a function for you (it works for your 3 requirements):

def list_replace(lst,elem,repl,n=0):
    ii=0
    if type(repl) is not list:
        repl = [repl]
    if type(elem) is not list:
        elem = [elem]
    if type(elem) is list:
        length = len(elem)
    else:
        length = 1
    for i in range(len(lst)-(length-1)):
        if ii>=n and n!=0:
            break
        e = lst[i:i+length]
        if e==elem:
            lst[i:i+length] = repl
            if n!=0:
                ii+=1
    return lst

I've tried with your examples and it works ok.

Tests made:

print list_replace([1,2,3], 3, [3, 4, 5])
print list_replace([1, 2, 3, 3, 3], 3, [8, 8], 2)
print list_replace([1, 2, 3, 3, 3], [2, 3], [8, 8], 2)

NOTE: never use list as a variable. I need that object to do the is list trick.

Neoares
  • 439
  • 11
  • 24
  • This is really bad. You shouldn't even be putting parentheses around `if`, `type(x) is y` should be `isinstance(x, y)`, and implicit type casting in Python never ends well (this won't work with sublists). – kirbyfan64sos Dec 09 '15 at 15:58
  • @kirbyfan64sos Why should I remove parenthesis and why should I use isinstance? – Neoares Dec 09 '15 at 16:01
  • 1
    @Neoares Convention, so that your code doesn't look strange to Python programmers. Have a read at https://www.python.org/dev/peps/pep-0008/ – Michiel Overtoom Dec 09 '15 at 16:29
  • @MichielOvertoom well maybe... I'm not used to python at the moment. Too much JavaScript. I'll edit the answer, thanks. – Neoares Dec 09 '15 at 16:39
  • As for the `isinstance`, it's because this code will break on subclasses of `list`. In addition, again, I would advise you to get rid of the casts completely and require that you're always handed lists. – kirbyfan64sos Dec 09 '15 at 16:42