Reputation: 133
Write a function that expects two lists of integers, a and b, as parameters and returns a list. The function should merge the elements of both input lists by index and return them as tuples in a new list. If one list is shorter than the other, the last element of the shorter list should be repeated as often as necessary. If one or both lists are empty, the empty list should be returned.
Please consider the following examples:
merge([0, 1, 2], [5, 6, 7]) # should return [(0, 5), (1, 6), (2, 7)]
merge([2, 1, 0], [5, 6]) # should return [(2, 5), (1, 6), (0, 6)]
merge([], [2, 3]) # should return []
You can assume that the parameters are always valid lists and you do not need to provide any kind of input validation.
My attempt:
def merge(a, b):
if len(a) == 0 or len(b) == 0:
mergelist = []
elif len(a) > len(b):
for i in range(0, len(b)):
mergelist = [a[i], b[i]]
for i in range(len(b), len(a)):
mergelist = [a[i], b[len(b)]]
elif len(b) > len(a):
for i in range(0, len(a)):
mergelist = [a[i], b[i]]
for i in range(len(a), len(b)):
mergelist = [a[len(a)], b[i]]
return mergelist
print(merge([0, 1, 2], [5, 6]))
Can someone tell me why my code is wrong? My IDE says
the list index is out of range
but i checked it over and over, it isn't,,,i think.
EDIT: I didn't think so many people would help me and give me their version of the code. They are extremely helpful. Thank you all so much!!!
Upvotes: 2
Views: 704
Reputation: 5033
A different implementation, used itertools.zip_longest
from itertools import zip_longest
def merge(a, b):
# check if list empty
if not a or not b:
return []
# merging
if len(a) == len(b):
return zip(a, b)
elif len(a) > len(b):
fill_term = b[-1]
else:
fill_term = a[-1]
return list(zip_longest(a, b, fillvalue=fill_term))
l = merge([1, 2, 3], [0, 4])
A zip
-free implementation. Shallow copy of list was used.
def merge(a, b):
# check if list empty
if not a or not b:
return []
# normalize the size of list
diff = abs(len(a) - len(b))
if len(a) > len(b):
fill_term = b[-1]
b = b.copy() # shallow copy
b.extend([fill_term]*diff)
elif len(a) < len(b):
fill_term = a[-1]
a = a.copy() # shallow copy
a.extend([fill_term]*diff)
# pairing
return [(a_term, b[i]) for i, a_term in enumerate(a)]
To avoid shallow copies, i.e. list.copy
, do for example b = b + [fill_term]*diff
to create a new list, same for a
Upvotes: 1
Reputation: 580
b[len(b)]
is out of range
and
a[len(a)]
is out of range
The last element should be b[len(b)-1]
and a[len(a)-1]
or
b[-1]
and a[-1]
Upvotes: 2
Reputation: 1458
Can someone tell me why my code is wrong
b[len(b)]
cause the issue. use the b[-1]
instead.
How about
def merge(a,b):
if len(a) == len(b) or not a or not b: return [ (x, y) for x, y in zip(a, b) ]
return [ (x, y) for x, y in zip(a + [a[-1]] * (len(b) - len(a)), b) ] if len(a) < len(b) else [ (x, y) for x, y in zip(a, b + [b[-1]] * (len(a) - len(b))) ]
Or
# if [(x,y)] and [(y,x)] are both acceptable result, then we can use this function.
def merge(a,b):
if len(a) == len(b) or not a or not b: return [ (x, y) for x, y in zip(a, b) ]
[a, b] = [a, b] if len(a) < len(b) else [b, a]
return [ (x, y) for x, y in zip(a + [a[-1]] * (len(b) - len(a)), b)]
Demo
>>> print(merge([0, 1, 2], [5, 6, 7])) # should return [(0, 5), (1, 6), (2, 7)]
[(0, 5), (1, 6), (2, 7)]
>>> print(merge([2, 1, 0], [5, 6])) # should return [(2, 5), (1, 6), (0, 6)]
[(2, 5), (1, 6), (0, 6)]
>>> print(merge([], [2, 3]))
[]
Upvotes: 1
Reputation: 802
This solution is fairy optimized as it is not extending a
and b
in memory.
def merge(a, b):
if not a or not b:
return []
merged_list = list(zip(a, b))
if len(a) < len(b):
merged_list += [(a[-1], bb) for bb in b[len(a):]]
elif len(a) > len(b):
merged_list += [(b[-1], aa) for aa in a[len(b):]]
return merged_list
Upvotes: 2
Reputation: 89
I am not sure what you are trying to do with your for loops inside your conditional statements and as a rule of thumb, we try to avoid the use of loops as much as possible although sometimes it is inevitable indeed.
So, your code breaks there b[len(b)] because indexes start at 0. Let's take one of your examples, you have b = [5,6], 5 (index 0), 6 (index 1) & len(b) = 2. Do you see why this cannot work? Fair mistake, we've all been there :)
My proposed solution:
def merge_unequal_lists(longest_list, shortest_list):
first_part = [(longest_list[i], shortest_list[i]) for i in
range(len(shortest_list))]
second_part = [(longest_list[i], shortest_list[-1]) for i in
range(len(shortest_list), len(longest_list))]
mergeList = first_part + second_part
return mergeList
def new_merge(a, b):
mergelist = []
if len(a) == 0 or len(b) == 0:
return mergelist
if len(a) == len(b):
mergeList = list(zip(a, b))
elif len(a) > len(b):
mergeList = merge_unequal_lists(a, b)
elif len(b) > len(a):
mergeList = merge_unequal_lists(b, a)
return mergeList
A few points, I really recommend that you start using the zip() function, very useful, especially for your use case. Also, list comprehensions are way more efficient than for loops, they come in very handy for any use case. Finally, following the DRY (Don't Repeat Yourself) principles, I tried to not repeat myself, hence the creation of an extra function, which does the same thing for two of your conditions, len(a) > len(b) & len(b) > len(a).
I hope this was helpful.
Upvotes: 2
Reputation: 16926
def merge(a,b):
if len(a) == 0 or len(b) == 0:
return []
elif len(a) >= len(b):
b = b + [b[-1]]*(len(a)-len(b))
else:
a = a + [a[-1]]*(len(b)-len(a))
return list(zip(a,b))
print(merge([0, 1, 2], [5, 6]))
print(merge([5, 6], [0, 1, 2]))
print(merge([0, 1, 2], [5, 6, 7]))
print(merge([2, 1, 0], [5, 6]))
print(merge([], [2, 3]))
output:
[(0, 5), (1, 6), (2, 6)]
[(5, 0), (6, 1), (6, 2)]
[(0, 5), (1, 6), (2, 7)]
[(2, 5), (1, 6), (0, 6)]
[]
Upvotes: 1
Reputation: 864
you can use zip()
for solving this problem
def merge(a,b):
zipped = list(zip(a,b))
if a and b:
if len(a) == len(b):
return zipped
else:
zipped.append((a[-1],b[-1]))
return zipped
else:
return []
print(merge([0, 1, 2], [5, 6]))
print(merge([5, 6], [0, 1, 2]))
print(merge([0, 1, 2], [5, 6, 7])) # should return [(0, 5), (1, 6), (2, 7)]
print(merge([2, 1, 0], [5, 6])) # should return [(2, 5), (1, 6), (0, 6)]
print(merge([], [2, 3])) # should return []
#output:
# [(0, 5), (1, 6), (2, 6)]
# [(5, 0), (6, 1), (6, 2)]
# [(0, 5), (1, 6), (2, 7)]
# [(2, 5), (1, 6), (0, 6)]
# []
but this function won't work if some of lists is longer than the other one by 2 or more items
Upvotes: 1
Reputation: 5637
There are a few things to update in your code:
(1) change this format mergelist = [a[i], b[i]]
to this format mergelist.append((a[i], b[i]))
(2) need to take care of the case when len(a) == len(b)
(3) list index is out of range because list index starts with zero. For example if len(a) is 2, the allowed index in range would be 0 and 1 only. So 2 is out of range.
Try this code:
def merge(a, b):
mergelist = []
if len(a) == 0 or len(b) == 0:
return mergelist
elif len(a) >= len(b): #takes care of len(a)==len(b)
for i in range(0, len(b)):
mergelist.append((a[i], b[i]))
for i in range(len(b), len(a)):
mergelist.append((a[i], b[len(b)-1])) #index minus 1
elif len(b) > len(a):
for i in range(0, len(a)):
mergelist.append((a[i], b[i]))
for i in range(len(a), len(b)):
mergelist.append((a[len(a)-1], b[i])) #index minus 1
return mergelist
print(merge([0, 1, 2], [5, 6]))
print(merge([5, 6], [0, 1, 2]))
print(merge([0, 1, 2], [5, 6, 7])) # should return [(0, 5), (1, 6), (2, 7)]
print(merge([2, 1, 0], [5, 6])) # should return [(2, 5), (1, 6), (0, 6)]
print(merge([], [2, 3])) # should return []
Output:
[(0, 5), (1, 6), (2, 6)]
[(5, 0), (6, 1), (6, 2)]
[(0, 5), (1, 6), (2, 7)]
[(2, 5), (1, 6), (0, 6)]
[]
Upvotes: 4