7

I have

 list = [a, b, c, d]

and

numbers = [2, 4, 3, 1]

I want to get a list of the type of:

new_list = [a, a, b, b, b, b, c, c, c, d]

This is what I have so far:

new_list=[] 
for i in numbers: 
    for x in list: 
        for i in range(1,i+1): 
            new_list.append(x)
Mike Müller
  • 82,630
  • 20
  • 166
  • 161
Ekaterina
  • 305
  • 1
  • 4
  • 10
  • @Ekaterina edit your question to include that piece of code, not in the comments. – hfz Jan 23 '17 at 16:41

9 Answers9

12

Here's one way to do it using zip, string multiplication and a list comprehension:

lst = ['a', 'b', 'c', 'd'] 
numbers = [2 , 4, 3, 1]

r = [x for i, j in zip(lst, numbers) for x in i*j]
print(r)
# ['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']

Pay attention to the choice of names when using Python. A name like list renders the builtin list function unusable.

If the items in lst are not strings you can simply use a nested comprehension on range, to duplicate the items in the list.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • 3
    OP didn't specify that `a`, `b`, etc are strings. Your solution only works based on the assumption that "`list`" contains a bunch of sequences. – mgilson Jan 23 '17 at 16:38
5

A nested list comprehension works:

L = ['a','b','c','d']
numbers = [2, 4, 3, 1]

>>> [x for x, number in zip(L, numbers) for _ in range(number)]
['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']

The "sub-loop" for _ in range(number) repeats the value number times. Here L can hold any object, not only strings.

Example:

L = [[1, 2, 3],'b','c', 'd']
numbers = [2, 4, 3, 1]
[x for x, number in zip(L, numbers) for _ in range(number)]
[[1, 2, 3], [1, 2, 3], 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']

but this flattens the sub list:

[x for i, j in zip(L, numbers) for x in i*j]
[1, 2, 3, 1, 2, 3, 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']

not exactly the desired result.

Mike Müller
  • 82,630
  • 20
  • 166
  • 161
5

As a general approach for any object (not only string) you can use itertools.repeat() within a generator expression:

def repeat_it(lst, numbers):
    return chain.from_iterable(repeat(i, j) for i, j in zip(lst, numbers))

Demo:

In [13]: from itertools import repeat, chain

In [21]: lst=[5,4,6,0]

In [22]: list(repeat_it(lst, numbers))
Out[22]: [5, 5, 4, 4, 4, 4, 6, 6, 6, 0]

In [23]: lst=['a','b','c','d']

In [24]: list(repeat_it(lst, numbers))
Out[24]: ['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']

Here is a benchmark on 3 main approaches. Note that the last one onley works for strings:

In [49]: lst = lst * 1000

In [50]: numbers = numbers * 1000

In [51]: %timeit list(chain.from_iterable(repeat(i, j) for i, j in zip(lst, numbers)))
1 loops, best of 3: 8.8 s per loop

In [52]: %timeit [x for x, number in zip(lst, numbers) for _ in range(number)]
1 loops, best of 3: 12.4 s per loop

In [53]: %timeit [x for i, j in zip(lst, numbers) for x in i*j]
1 loops, best of 3: 7.2 s per loop
Mazdak
  • 105,000
  • 18
  • 159
  • 188
3

You can use numpy.repeat() as another option:

import numpy as np
np.repeat(lst, numbers).tolist()

# ['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']
Psidom
  • 209,562
  • 33
  • 339
  • 356
1

Another way to do it with a loop would be:

new_list = []
for number, item in zip(numbers, l):
    for i in range(number):
        new_list.append(item)

Now we have:

new_list = ['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']
Christopher Shroba
  • 7,006
  • 8
  • 40
  • 68
1

If you are unsure of how list comprehensions work,

myList=['a','b','c','d'] # not a good idea to use list as a name for your variable
numbers=[2,4,3,1]
new_list=[]
for i in range(len(myList)):     
    for j in range(numbers[i]):          
        new_list.append(myList[i])

print(new_list)
hfz
  • 401
  • 4
  • 16
1

This will work regardless of whether a, b, c and d are variables or strings:

a = 1
b = 2.0
c = "cheese"
d = ["c", "a", "k", "e"]

lst = [a, b, c, d]
numbers = [2, 4, 3, 1]

# if len(lst) == len(numbers):
new_lst = [i for i, j in zip(lst, numbers) for k in range(j)]

You might want to uncomment the if statement (and indent the line underneath) to check if the lists have the same length, otherwise new_lst will only contain as many items as the shorter list.

This, this and the documentation section on nested list comprehensions are worth reading.

Community
  • 1
  • 1
Spherical Cowboy
  • 565
  • 6
  • 14
1

That's my solution, just to add a different one.

l = ['a', 'b', 'c', 'd']
n = [2, 4, 3, 1]
r = []
for i,v in enumerate(l):
    r += list(v*n[i])

>>> r
    ['a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'd']
alec_djinn
  • 10,104
  • 8
  • 46
  • 71
0

Assuming that both lists are the same length and the second is always a list of numbers, here is a solution without using zip or any imports:

lst = ['a', 'b', 'c', 'd']
numbers = [2,4,3,1]

result = sum([[lst[i]]*numbers[i] for i in range(len(lst))],[])
Nick is tired
  • 6,860
  • 20
  • 39
  • 51