4

Is there a method similar to str.replace which can do the following:

>> replace(sequence=[0,1,3], old=[0,1], new=[1,2]) 
[1,2,3]

It should really act like str.replace : replacing a "piece" of a sequence by another sequence, not map elements of "old" with "new" 's ones. Thanks :)

2 Answers2

4

No, I'm afraid there is no built-in function that does this, however you can create your own!

The steps are really easy, we just need to slide a window over the list where the width of the window is the len(old). At each position, we check if the window == to old and if it is, we slice before the window, insert new and concatenate the rest of the list on after - this can be done simply be assigning directly to the old slice as pointed out by @OmarEinea.

def replace(seq, old, new):
    seq = seq[:]
    w = len(old)
    i = 0
    while i < len(seq) - w + 1:
        if seq[i:i+w] == old:
            seq[i:i+w] = new
            i += len(new)
        else:
            i += 1
    return seq

and some tests show it works:

>>> replace([0, 1, 3], [0, 1], [1, 2])
[1, 2, 3]
>>> replace([0, 1, 3, 0], [0, 1], [1, 2])
[1, 2, 3, 0]
>>> replace([0, 1, 3, 0, 1], [0, 1], [7, 8])
[7, 8, 3, 7, 8]
>>> replace([1, 2, 3, 4, 5], [1, 2, 3], [1, 1, 2, 3])
[1, 1, 2, 3, 4, 5]
>>> replace([1, 2, 1, 2], [1, 2], [3])
[3, 3]

As pointed out by @user2357112, using a for-loop leads to re-evaluating replaced sections of the list, so I updated the answer to use a while instead.

Joe Iddon
  • 20,101
  • 7
  • 33
  • 54
  • I think you can use `seq[i:i+w] = new` instead of `seq = seq[:i] + new + seq[i+w:]` – Omar Einea Jan 29 '18 at 18:44
  • 1
    [This has problems with rescanning the replaced sections of the strings](https://ideone.com/y2Rcks). (Also, it's algorithmically inefficient, though implementing a better algorithm without eating a bunch of interpreter overhead is difficult.) – user2357112 Jan 29 '18 at 18:45
  • 1
    @user2357112 Thanks for pointing that problem out, I will re-write it now :) – Joe Iddon Jan 29 '18 at 18:47
  • @user2357112 I have updated the answer and it does now pass your test case but there may be something else wrong (:/). What did you mean by "it's algorithmically inefficient"? Did you simply mean because it was re-evaluating replaced sections? – Joe Iddon Jan 29 '18 at 18:53
  • @JoeIddon: [Still not quite right.](https://ideone.com/xto4Hu) It needs to jump ahead by the length of `new`, not `old`. I think that's the only remaining bug, but I'm not sure. As for the algorithmic inefficiency, this code implements a naive string search algorithm, taking O(mn) time where m = len(old) and n = len(seq) (not counting the time taken to perform replacement). There are [algorithms that can do much better](https://en.wikipedia.org/wiki/String_searching_algorithm), but Python implementations of them might not win in practice due to interpreter overhead. – user2357112 Jan 29 '18 at 19:00
  • The replacement could also be written to avoid shifting the tail of the list so much when `len(new) != len(old)`, most simply by building a new list instead of slice-assigning into the old. Again, this may not win in practice due to interpreter overhead. – user2357112 Jan 29 '18 at 19:03
  • @user2357112 Thanks for the pointers, it would be interesting to try and write the algorithm in `C` and see the actual change. I will also give that Wiki article a read as it looks good :) – Joe Iddon Jan 29 '18 at 19:11
-1

I tried this but before using this method read this about eval() by Ned :

import re
import ast

def replace(sequence, old, new):
    sequence = str(sequence)
    replace_s=str(str(old).replace('[', '').replace(']', ''))
    if '.' in replace_s:
        replace_ss=list(replace_s)
        for j,i in enumerate(replace_ss):
            if i=='.':
                try:
                    replace_ss[0]=r"\b"+ replace_ss[0]
                    replace_ss[j]=r".\b"
                except IndexError:
                    pass
        replace_s="".join(replace_ss)


    else:

        replace_s = r"\b" + replace_s + r"\b"


    final_ = str(new).replace('[', '').replace(']', '')
    return ast.literal_eval(re.sub(replace_s, final_, sequence))





print(replace([0, 1, 3], [0, 1], [1, 2]))

output:

[1, 2, 3]
Aaditya Ura
  • 12,007
  • 7
  • 50
  • 88
  • @user2357112 , give the correct input as OP asked. In your example there is no old string in sequence. – Aaditya Ura Jan 29 '18 at 19:15
  • There is no such thing as "the correct input"; answers are supposed to work for more than just the particular example input given in the question. Your code does not. – user2357112 Jan 29 '18 at 19:16
  • @user2357112 yes but if you are making a program for prime no then don't expect factorial from it. check what OP asked for. there is old int numbers in sequence and he want to replace those int with new int , but in your example there is no old int in sequence. – Aaditya Ura Jan 29 '18 at 19:18
  • [It doesn't magically start working if we throw in an instance of the `old` list in `sequence`.](https://ideone.com/lG3q8w) – user2357112 Jan 29 '18 at 19:21
  • @user2357112 check out now. – Aaditya Ura Jan 29 '18 at 19:43
  • [Still fails.](https://ideone.com/f5aOz0) It also collapses in a wide variety of ways for inputs that aren't simple lists of integers, although I doubt I'll be able to convince you the question is asking for support for general sequences and not just lists of integers. – user2357112 Jan 29 '18 at 19:54
  • @user2357112 talk is cheap show me the code , and give me more test case i am enjoying ;) check now – Aaditya Ura Jan 29 '18 at 20:00
  • @user2357112 waiting for next ;) – Aaditya Ura Jan 29 '18 at 20:22