0

I tried to define a function on python to merge the strings on a given index range, from a list of strings. Here is my code:

def merge_some(L,i,j):
    result=L[:]
    merging=''
    for k in range(i,j+1):
        merging +=L[k]
    result[i:j+1]=merging
    return result

trial= ['a','b','c','d','e','f']
print(merge_some(trial, 1,3))  #index 3 included

The output should be:

['a','bcd','d','e','f']

The code should work fine, but if I run it, I just get back the original list, which is quite strange to me. Whilst I know there's plenty of methods to compute such function (e.g. using the .join() method instead of looping), I would like to ask if somebody has any idea of the cause of such weird behaviour. Any idea would be very much appreciated. Many thanks!

Akai M
  • 3
  • 2
  • maybe use `print()` to see what you have in variables. – furas Dec 31 '20 at 17:55
  • I don't know why your slice assignment doesn't work, but this does: `result=result[:i]+[merging]+result[j+1:]` (Edit: this works too: `result[i:j+1]=[merging]`) – RufusVS Dec 31 '20 at 17:57
  • The actual reason is that merging is a string, which becomes a list of letters during the slice assignment! so "bcd" looks like this: ['b','c','d'] so you ended up replacing what was already there!!! – RufusVS Dec 31 '20 at 18:11

4 Answers4

3

str.join() should fox this for you:

def merge_some(origin_list, from_index, to_index):
    beginning = origin_list[: from_index]
    merged_items_wrapped = [''.join(origin_list[from_index + 1: to_index + 1])]
    end = origin_list[to_index+1:]
    return beginning + merged_items_wrapped + end

test_list = ['a','b','c','d','e','f']
print(merge_some(test_list, 1, 3))
>>> ['a','bcd','e','f']

Note: you miscalculated the indexes when suggesting the expected result (therefore you added j+1, which to my eyes seems not very intuitive) , but in case you meant that you want many items and not 'to' just do:

def merge_some(origin_list, from_index, num_of_items):
    beginning = origin_list[: from_index]
    merged_items_wrapped = [''.join(origin_list[from_index + 1: num_of_items - 1])]
    end = origin_list[from_index + num_of_items - 1:]
    return beginning + merged_items_wrapped + end

trial= ['a','b','c','d','e','f']
print(merge_some(trial, 1,3))
>>> ['a','bcd','e','f']
adir abargil
  • 5,495
  • 3
  • 19
  • 29
2

You can do something like this:

def merge_some(L,i,j):
    result=L[:]
    result[i:j+1]=[''.join(L[i:j+1])]
    return result


>>> merge_some(['a','b','c','d','e','f'], 1,3)
['a', 'bcd', 'e', 'f']

There are two things to note:

  1. The part result[i:j+1] is a slice assignment that replaces the contents of the slice with the contents of the RH side (even if a different size than the slice);
  2. The ''.join(L[i:j+1]) creates a single string from those elements;
  3. The [...] around [''.join(L[i:j+1])] makes that string act as a single element for the slice assignment.
dawg
  • 98,345
  • 23
  • 131
  • 206
0

First of all, many thanks to everybody for your help. While I knew already the .join() method, as mentioned in my question, I just spotted what was missing in my code and the reason of that strange behaviour:

def merge_some(L,i,j):
    try:
        result=L[:]
        merging=''
        for k in range(i,j+1):
            merging +=L[k]
        result[i:j+1]=[merging]
        return result
    except:
        return []

trial= ['a','b','c','d','e','f']
print(merge_some(trial, 1,3))   #The index=3 is also merged in the script

Therefore what was missing is [merging], i.e. slice assignment has to be declared as an iterable.

Akai M
  • 3
  • 2
  • The actual reason is that merging is a string, which becomes a list of letters during the slice assignment! so "bcd" looks like this: ['b','c','d'] so you ended up replacing what was already there!!! Makes sense now! – RufusVS Dec 31 '20 at 18:11
0

The way you use the parameter j goes against common practice. It would be more intuitive to not add one to j and instead pass 1 to 4 as arguments when calling merge_some.

The result you get in your question is due to line 6.

The other issue with the code in your question and your answer is that when you pass an empty list you go out of bounds, so you have to add a check for that.

I would suggest the following solution as it also works with empty lists :

def merge_some(values, start, end):
    return list(filter(None, values[:start] + [''.join(values[start:end])] + values[end:]))

letters = ['a', 'b', 'c', 'd', 'e', 'f']
print(merge_some(letters, 1, 4))

Note that I am passing 1 to 4 as parameters.

Result :

>>> ['a', 'bcd', 'e', 'f']
  • Thanks, I edited, now works also for empty lists. Though I don't understand your point about the index, if one wishes the last index in the function to be also included, well, I can't find anything wrong in that. – Akai M Jan 01 '21 at 10:56
  • You can do anything you like, but the point is that it is usually better to align with common practice. When someone uses the function he maybe confused. You will then have to write a comment for the function to explain the different behavior, but many people tend not to read comments. – Michael Trikergiotis Jan 01 '21 at 11:53