3

What are some good ways to define a tuple consisting of integers where the number of occurrences of each item is known ?

For example,

I want to define a tuple with 3 2's, 2 4's and 1, 3, 5 occur once.

For this, I can always go the manual way :

foo = (1, 2, 2, 2, 3, 4, 4, 5)

However, this becomes a bit messy when the number of items in the list is large. So, I want to know what are some ways to automate the task of generating the desired number of duplicates of each item.

Kshitij Saraogi
  • 4,761
  • 5
  • 33
  • 57

7 Answers7

4

You can do it like this:

>>> (1,) * 1 + (2,) * 3 + (4,) * 2 + (5,) * 1
(1, 2, 2, 2, 4, 4, 5)
Dan D.
  • 67,516
  • 13
  • 93
  • 109
2

One way is to use sequence multiplication. Here's a simple version that makes no attempt to avoid creating unnecessary intermediate objects:

accumulator = ()
for (val, count) in some_data_structure:
    accumulator += (val,) * count

That can be improved, the main point is to demonstrate that (1,) * 5 gives you (1, 1, 1, 1, 1). Note that this copies the object reference - that's fine for integers, but can cause confusion if you're trying to multiply a sequence of mutable objects.

Peter DeGlopper
  • 32,557
  • 6
  • 77
  • 74
2

If you have a tuple of tuples denoting the value and frequency, you can do the following:

tuples = ((1,1), (2,3), (3,1), (4,2), (5,1))

tuple(i for i, n in tuples for _ in range(n)) # Use xrange in Python 2.X
# (1, 2, 2, 2, 3, 4, 4, 5)

Or, if you know that the values are always going to be 1, 2, 3, ..., n, you can use enumerate with a tuple of the frequencies.

freqs = (1, 3, 1, 2, 1)

tuple(i for i, n in enumerate(freqs, 1) for _ in range(n))
# (1, 2, 2, 2, 3, 4, 4, 5)

If you're curious about the use of the double comprehension in the generator expression, you may want to check out this question.

Community
  • 1
  • 1
Jared Goguen
  • 8,233
  • 2
  • 13
  • 33
  • I wasn't the downvoter, but maybe a little explanation of what's going on would help? This is basically the same idea as in my answer, making one of the improvements I alluded to to avoid creating some of the unnecessary intermediate tuples. – Peter DeGlopper May 11 '16 at 16:54
  • This is an overkill for such a simple task. The complexity of `sum(..., [])` is quadratic. – vaultah May 11 '16 at 16:54
  • @vaultah Fair enough, it does generalize the problem though. The nested generators should avoid the extra complexity. I'll remove the `sum` approach. – Jared Goguen May 11 '16 at 17:05
2

If your tuple has not many number, you can do it in the simplest way.

(1,)+(2,)*3+(3,)+(4,)*2+(5,)

Otherwise, just turn it into a function.

def myTuple(*val):
    return sum(((i,) * n for i, n in val), ())

myTuple((1,1),(2,3),(3,1),(4,2),(5,1))
>>>(1, 2, 2, 2, 3, 4, 4, 5)

you can also call it with:

val = ((1,1),(2,3),(3,1),(4,2),(5,1))
myTuple(*val)
>>>(1, 2, 2, 2, 3, 4, 4, 5)
xirururu
  • 3,852
  • 6
  • 28
  • 48
0

Something like this could work:

>>> result = tuple()
>>> for item, repeat in ((1, 1), (2, 3), (3, 1), (4, 2), (5, 1)):
...    result = result + (item,) * repeat
>>> result
(1, 2, 2, 2, 3, 4, 4, 5)
aychedee
  • 22,113
  • 7
  • 74
  • 81
0

So you want the inverse function of collections.Counter. Here is how you could do it,

# make a dict of counts (list of tuples is better)
counts = {1: 1, 2: 3, 4: 2, 3:1, 5: 1}
t = tuple(k for k,v in sorted(counts.items()) for _ in range(v))
(1, 2, 2, 2, 3, 4, 4, 5)
# for k,v in list_of_tuples, for a list of tuples
C Panda
  • 2,749
  • 1
  • 9
  • 11
0

You can define the following function

def a_tuple(*data):
    l = []
    for i, cnt in data: l.extend([i]*cnt)
    return tuple(l)

and use it like this

print(a_tuple((1,1), (2,3), (3,1), (4,2), (5,1)))

to produce the following output

(1, 2, 2, 2, 3, 4, 4, 5)

Have a look to the .extend() method of list if you don't understand how the function works.

gboffi
  • 17,041
  • 5
  • 45
  • 76