6

Suppose I have those two lists

a=[0,1,6,4,2,8] 
b=[10,20,30,40]

I want to replace elements in list a that are between 0 and 4 with items from list b.

In other words, my output should be

[10,20,6,30,40,8]

Note that essentially, we strip list a of elements satisfying a certain condition and simply replacing it with an item from list b onto this. (The ordering in b is maintained)

EDIT

I actually know that exactly N number of items in list a satisfy the condition imposed. Then I need to "replace" these in-place with another list b of size N.

scharette
  • 9,437
  • 8
  • 33
  • 67
Vinayak Suresh
  • 165
  • 1
  • 6

6 Answers6

5

Considering your edit, I would suggest using this simple and readable for loop.

a=[0,1,6,4,2,8] 
b=[10,20,30,40]
upper_bound = 4
lower_bound = 0
exclusions = range(lower_bound,upper_bound+1)

for index,item in enumerate(a):
  if item in exclusions:
    a[index] = b.pop(0)

print(a)
>>>>[10, 20, 6, 30, 40, 8]

Basically, here I'm iterating over a copy of the a list and once I find an item in the range of my specified bounds I pop the first element of b feeding it back to a.

Important Note

It is important to understand that I used an exclusions list, which I agree can be ommitted in your particular case, in order to allow someone to add string objects to the list and still have the same kind of behavior. Obviously, you could use something like,

if item > lower_bound and item < upper_bound

Which would be more efficient for your specific case since you're not creating an unecessary list.


To answer @Julian concerns, let's say your lists becomes string objects,

a=['a','b','c','d','e','f'] 
b=['test','test','test','test']

And instead of replace 0 to 4, you want to replace a c to f. With my solution it would go like this,

exclusions = ['c','d','e','f']

for index,item in enumerate(a):
    if item in exclusions:
    a[index] = b.pop(0)

print(a)
>>>>['a', 'b', 'test', 'test', 'test', 'test']

With @Julian answer,

b_iter = iter(b)
print([item if item < 0 or item > 4 else b_iter.next() for item in a])
>>>> TypeError: '<' not supported between instances of 'str' and 'int'

Now as he said, > and < operators can be used to compare two strings, so I mean he could go and change his comparisons to string type,

b_iter = iter(b)
print([item if item < '0' or item > '4' else b_iter.next() for item in a])
>>>>['a', 'b', 'c', 'd', 'e', 'f']

that would still be completely wrong because comparing objects other than int or float with < and > makes no sense in the context of OP's question.

Also, it becomes even more apparent when you use other types,

a=[{1,2},{2,3},{3,4},{4,5},{5,6},{6,7}] 
b=[{'test'},{'test'},{'test'},{'test'}]

exclusions = [{3,4},{4,5},{5,6},{6,7}]

for index,item in enumerate(a):
  if item in exclusions:
    a[index] = b.pop(0)

print(a)
>>>> [{1, 2}, {2, 3}, {'test'}, {'test'}, {'test'}, {'test'}]

b_iter = iter(b)
print([item if item < 0 or item > 4 else b_iter.next() for item in a])
>>>> TypeError: '<' not supported between instances of 'set' and 'int'

Now, I wouldn't know how he would go and fix that following his logic.

So as I said, in his comment section, make sure to be aware of what his answer implies.

scharette
  • 9,437
  • 8
  • 33
  • 67
  • it's a nice one 1 up – Raman Mishra Jul 30 '18 at 19:16
  • 1
    you don't need the `[:]` in `enumerate` just `a`. I would say this is the more Pythonic answer – LinkBerest Jul 30 '18 at 19:30
  • I was gonna say that I prefer the `exclusion` syntax for readability at a small cost of creating an extra list. But your enumerate feedback is really interesting. Are we sure about it though. Could give a lit bit more insight on to why I don't need to iterate over a copy when using enumerate ? – scharette Jul 30 '18 at 19:36
  • @JGreenwell I actually opened a [thread](https://stackoverflow.com/q/51601249/7692463) about it since I really want to understand. – scharette Jul 30 '18 at 19:51
  • enumerate is a generator so it will just take a sequence (any sequence or iterator actually) and just "generates" an incrementing index to be yielded with each item from the passed sequence (so it's not making a copy anyway just an enumerate object which it is moving through). If you want to know more check out the [PEP from when it was added](https://www.python.org/dev/peps/pep-0279/). The exclusion is fine (actually I thought about it and it depends on too many things whether I would use an exclusion range or try/except) – LinkBerest Jul 30 '18 at 19:52
  • This is not very efficient from a memory standpoint. Imagine if `upper_bound` and `lower_bound` have a HUGE difference between them. You would need to store a list that large in memory and each item for existence in that list every iteration of the for loop. – Julian Jul 30 '18 at 20:21
  • @julian I entirely agree with you. But I like to think that people should care about performance and memory when it really matters. My idea of creating a list was to allow op to use a similar logic using other types like `string` for example. I made it general and readable because the question title is general and that could ne benificial for future users. – scharette Jul 30 '18 at 20:25
  • @scharette right, but instead of creating a list for basically no reason, what would be cleaner is simply doing comparisons in place: like `if item < lower_bound or item > upper_bound:` instead of creating the `exclusions` list and performing an ` in :` comparison – Julian Jul 30 '18 at 20:36
  • @Julian read my above comment or the one your answer. I think you'll get what I wanted to provide as an answer. Also added a note. – scharette Jul 30 '18 at 20:48
  • Note, just an fyi not a big deal, for Python 3 you need `next(b_iter)` instead of `b_iter.next()`- the next operator was dropped for the function - for the list comprehension to work. – LinkBerest Aug 07 '18 at 15:32
0

you can do like this.

list1=[0,1,6,4,2,8]
list2=[10,20,30,40]
j=0
ans=[0]*len(list1)
for i in range(len(list1)):
    if list1[i] <= 4:
        ans[list1.index(list1[i])]+=list2[j]
        if(j<len(list2)):
            j+=1
    else:
        ans[i]+=list1[i]

print(ans)

that should work.

Raman Mishra
  • 2,635
  • 2
  • 15
  • 32
  • I get the idea thanks. Also, your code seems to have a number of syntactical errors. Is this because of a different python version – Vinayak Suresh Jul 30 '18 at 19:03
  • i have made the changes you can run this code it will work fine on python 3.6 – Raman Mishra Jul 30 '18 at 19:14
  • 3
    @RamanMishra Make sure to value readability when you code. In fact, ask yourself if you were a complete stranger looking at your code would you understand what is the purpose of it? – scharette Jul 30 '18 at 19:16
  • 1
    yaa actually you are right and it's just because it's been a while i used python that's why my code is bit mess i will try to make it more readable thanks for feedback. @scharette – Raman Mishra Jul 30 '18 at 19:18
0

You can use a counter on your list b, and an if statement to replace only numbers less than 4:

a=[0,1,6,4,2,8]
b=[10,20,30,40]
n=0

for i in range(len(a)):
    if a[i] <= 4 and a[i] >= 0:
        a[i] = b[n]
        n += 1
Jay Heine
  • 31
  • 5
0

I think this is a more compact and readable code for you.

a = [0, 1, 6, 4, 2, 8]
b = [10, 20, 30, 40]

temp = b
c = []
for item in a:
    if 0 <= item <= 4:
        c.append(temp.pop(0))
    else:
        c.append(item)

print(c)
Hermis14
  • 214
  • 3
  • 12
0

You can use list comprehensions and an iterator to accomplish this in two lines:

>>> b_iter = iter(b)
>>> [item if item < 0 or item > 4 else b_iter.next() for item in a]
[10, 20, 6, 30, 40, 8]

This is all assuming your assumption that a contains N items where 0 < item < 4 and that the length of b is also exactly N.

This method has two benefits to the most highly upvoted answer here:

  1. There is no need for an exclusions list, which is just a waste of memory.
  2. This does not modify the a list, which could lead to issues if that is not what you are meaning to do.
Julian
  • 1,078
  • 5
  • 17
  • 1) You should note that this only works for `int` type and is at the cost of readabilty over my answer. – scharette Jul 30 '18 at 20:39
  • 2) I'm modifying the `a` list because this is what OP is asking (_we strip list a of elements satisfying a certain condition and simply replacing it with an item from list b onto this._). He gains nothing more from your answer if this statement is correct. – scharette Jul 30 '18 at 20:40
  • @scharette not only are you incorrect, but you are incorrect in two different ways... 1) the solution I proposed does NOT only work for `int`, in fact it works for `float`, `int`, `str`, or anything else that can be compared with `<` or `>` 2) Your solution DOES in fact only work for `int` as `range` specifically returns a value of integers see the help text for range... `range(start, stop[, step]) -> list of integers` – Julian Aug 06 '18 at 21:01
  • I used `range()` because the specific case of OP needed handling for `int` and it is a neat way of create a **list of ints**. But in my answer you could use `string` type like a **list of words** and it would still work. In your case, you're right that `str` support `<` but this is not in any way the behavior that someone would want. I mean, the goal is to replace certain values that you can specify in my `exclusions` list. Your answer would compare two string values with the `<` which is completely odd in the context of OP. – scharette Aug 07 '18 at 12:26
  • As I said above above, your answer is efficient, but specific to `int` and possibly `float` values and you should definitely disclaim it. Now, if you decide not to, I can't suggest something else to improve the quality of your answer. – scharette Aug 07 '18 at 12:29
  • In fact, I went and edit my answer so you can have better understanding of my point. Hope it clarifies ! – scharette Aug 07 '18 at 12:51
-1

The main concept behind solving this is to iterate through list a, check to see if each value achieves a certain condition (in this case, being between 0 and 4), and if so, replace that value with the corresponding value from b. You can use a for-loop and if statements to achieve this.

for i in range(len(a) + 1):   # where a is your first list
    if a[i]==1: #checking a specific index of the list
        a[i] = 10
    elif a[i] == 2:
        a[i] = 20
    elif a[i] == 3:
        a[i] == 30
    elif a[i] == 4:
        a[i] == 40
    else:
        continue

Hope that helps.

ab123
  • 357
  • 4
  • 17
  • You state that the OP only wants to replace the item based on a condition (between 0 & 4) but then your code fails to check this. Also, a set of if/elif/else statements can indicate that you either need a dictionary or to re-look at your logic. I would check out [Jay's answer](https://stackoverflow.com/questions/51600485/replacing-list-elements-that-satisfy-some-condition-with-items-from-another-list/51600895#51600895) as it is a straight-forward look at the logic for replacing this with just a single if and counter variable. (note: did not downvote but code needs to work or show concept) – LinkBerest Jul 30 '18 at 19:44