lxyu
lxyu

Reputation: 2839

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.)

Upvotes: 1

Views: 145

Answers (5)

lvc
lvc

Reputation: 35059

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

Upvotes: 0

lxyu
lxyu

Reputation: 2839

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]

Upvotes: 0

jurgenreza
jurgenreza

Reputation: 6086

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]

Upvotes: 0

Gareth Latty
Gareth Latty

Reputation: 88977

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.

Upvotes: 1

Blender
Blender

Reputation: 298056

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)

Upvotes: 2

Related Questions