I have a Python function w/ specified functionality, and two proposed approaches - which do you prefer, and why? (performance, simplicity, clarity, etc) (both use import random
)
PROBLEM: Need a plain Python function that accepts an arbitrary number of iterables (tuples, lists, dictionaries), and returns them shuffled in the same order:
a = (1, 2, {3: 4}, 5)
b = [(5,6), [7,8], [9,0], [1,2]]
c = {'arrow': 5, 'knee': 'guard', 0: ('x',2)}
x, y, z = magic(a, b, c)
print(x, y, z, sep='\n')
# ({3: 4}, 1, 2)
# [[9, 0], (5, 6), [7, 8]]
# {0: ('x', 2), 'arrow': 5, 'knee': 'guard'}
The function must:
- Return iterables shuffled in the same order (see above)
- Accept any number of iterables
- Preserve iterables types
- Support nested iterables of any depth and type
- Not shuffle nested elements themselves (eg.
[7,8]
above doesn't become[8,7]
) - Return iterables with length of shortest iterable's length w/o raising error (see above)
- Return shuffled iterables in the same order they're passed in (see above)
NOTE: requires Python 3.7. Can work with Python 3.6- if excluding dicts, as dicts aren't ordered.
SOLUTION 1:
def ordered_shuffle(*args):
args_types = [type(arg) for arg in args] # [1]
_args = [arg if type(arg)!=dict else arg.items() for arg in args] # [2]
args_split = [arg for arg in zip(*_args)] # [3]
args_shuffled = random.sample(args_split, len(args_split)) # [4]
args_shuffled = map(tuple, zip(*args_shuffled)) # [5]
return [args_types[i](arg) for i, arg in enumerate(args_shuffled)] # [6]
Explanation: img
SOLUTION 2:
def shuffle_containers(*args):
min_length = min(map(len, args))
idx = list(range(min_length))
random.shuffle(idx)
results = []
for arg in args:
if isinstance(arg, list):
results.append([arg[i] for i in idx])
elif isinstance(arg, tuple):
results.append(tuple(arg[i] for i in idx))
elif isinstance(arg, dict):
items = list(arg.items())
results.append(dict(items[i] for i in idx))
else:
raise ValueError(
"Encountered", type(arg),
"expecting only list, dict, or tuple"
)
return results