How to "Zip" or "Rotate" a Variable Number of Lists

How to zip or rotate a variable number of lists?

You can roll your own ZipMany instance which manually iterates each of the enumerations. This will likely perform better on larger sequences than those using GroupBy after projecting each sequence:

public static IEnumerable<TResult> ZipMany<TSource, TResult>(
IEnumerable<IEnumerable<TSource>> source,
Func<IEnumerable<TSource>, TResult> selector)
{
// ToList is necessary to avoid deferred execution
var enumerators = source.Select(seq => seq.GetEnumerator()).ToList();
try
{
while (true)
{
foreach (var e in enumerators)
{
bool b = e.MoveNext();
if (!b) yield break;
}
// Again, ToList (or ToArray) is necessary to avoid deferred execution
yield return selector(enumerators.Select(e => e.Current).ToList());
}
}
finally
{
foreach (var e in enumerators)
e.Dispose();
}
}

Using zip() to rotate a list

data = [["date_a", "1a", "2a", "3a"], 
["date_b", "1b", "2b", "3b"]]

print zip(*(zip(itertools.repeat(ls[0]), ls[1:]) for ls in data))

gives

[(('date_a', '1a'), ('date_b', '1b')),
(('date_a', '2a'), ('date_b', '2b')),
(('date_a', '3a'), ('date_b', '3b'))]

See comments for some useful variations.

Common Lisp: Zip arbitrary number of lists

For an arbitray number of lists, you'd need to get rid of apply #'mapcar, because otherwise the number of lists would be limited by call-arguments-limit.

One typical way would be to use reduce instead, combining two lists at a time:

(defun zip (list-of-lists &key (combiner #'concat))
(reduce (lambda (list-a list-b)
(mapcar combiner list-a list-b))
list-of-lists))

If you don't like the explicit lambda form, you might like curry, e. g. from alexandria:

(defun zip (list-of-lists &key (combiner #'concat))
(reduce (curry #'mapcar combiner)
list-of-lists))

Other looping constructs are loop, do, dolist, and there are also a few looping libraries, e. g. iterate, for.

How to combine more than two generic lists in C# Zip?

The most obvious way for me would be to use Zip twice.

For example,

var results = l1.Zip(l2, (x, y) => x + y).Zip(l3, (x, y) => x + y);

would combine (add) the elements of three List<int> objects.

Update:

You could define a new extension method that acts like a Zip with three IEnumerables, like so:

public static class MyFunkyExtensions
{
public static IEnumerable<TResult> ZipThree<T1, T2, T3, TResult>(
this IEnumerable<T1> source,
IEnumerable<T2> second,
IEnumerable<T3> third,
Func<T1, T2, T3, TResult> func)
{
using (var e1 = source.GetEnumerator())
using (var e2 = second.GetEnumerator())
using (var e3 = third.GetEnumerator())
{
while (e1.MoveNext() && e2.MoveNext() && e3.MoveNext())
yield return func(e1.Current, e2.Current, e3.Current);
}
}
}

The usage (in the same context as above) now becomes:

var results = l1.ZipThree(l2, l3, (x, y, z) => x + y + z);

Similarly, you three lists can now be combined with:

var results = list1.ZipThree(list2, list3, (a, b, c) => new { a, b, c });

how to store values from a list of lists into a separate list in python?

What you're really looking to do is rotate a 2d list. There is a great answer
here about how to do that, but I'll summarize the main idea behind it.

If x is the array you provided, i.e.

x = [[apple,  dog,  phone,    water, black], 
[banana, cat, laptop, milk, pink],
[melon, bird, computer, juice, green]]

Then all you need is

rotated = list(zip(*x))
fruits = rotated[0]
animals = rotated[1]
# etc.

What this does is as follows:

  • *x unpacks x, so that zip is called with

    zip([apple, dog, ...], [banana, cat, ...], [melon, bird, ...])
  • zip then pulls apart the inner lists, and constructs a zip object.
  • list converts it back to a list.

This assumes that fruit will always be at index 0, animal will always be at index 1, etc.

If that's not the case, then you would have to build a set of values for each category, and then iterate through the list manually to sort it out.

knownFruits = {banana, melon, apple, pineapple, guava}
knownAnimals = {dog, cat, bird, leopard, shark}
fruits = []
animals = []

for row in x:
for elem in row:
if elem in fruits:
fruits.append(elem)
elif elem in animals:
animals.append(elem)
#etc.

How Python 3 zip list/tuple unpacking works under the hood?

Eureka!

While I was trying to write my question clearly, I stumbled upon this answer Why does x,y = zip(*zip(a,b)) work in Python?, which gave me a hint that I still did not get before trying it in a REPL. The key is unpacking the result of zip:

print(*zip(l1,l2))
# Output: (1, 'a') (2, 'b') (3, 'c')

We get 3 tuples of 2 elements, which are treated like 3 lists of 2 elements passed as arguments to zip:

l1 = [ 1 , 2 ]
l2 = ['a','b']
l3 = ['+','-']

print(list(zip(l1,l2,l3)))
# Output: [(1, 'a', '+'), (2, 'b', '-')]

※ Note that if you use 3 lists of 4 elements, zip will output 4 lists of 3 elements

l1 = [ 1 , 2 , 3 , 4 ]
l2 = ['a','b','c','d']
l3 = ['+','-','*','/']

print(list(zip(l1,l2,l3)))
# Output: [(1, 'a', '+'), (2, 'b', '-'), (3, 'c', '*'), (4, 'd', '/')]

matrix rotation

I now think of it as rotating values in a matrix 90° to make rows into columns and columns into rows.

It is like rotating a matrix by providing the rows as arguments instead of the matrix itself.

multiple variable assignment and tuple/list unpacking

The last piece of the puzzle is to unpack the result and assign each tuple in its own variable using multiple assignment!

l1 = [1,2,3]
l2 = ['a','b','c']
l3,l4= zip(*zip(l1,l2))

print('l3= ',l3)
print('l4= ', l4)
# Output: l3= (1, 2, 3)
# l4= ('a', 'b', 'c')

Epilogue

I decided to keep writing this Q&A to learn by explaining what I had discovered by experimenting, after not immediately getting the meaning of the answer I found while writing.

I also hope that my analogy with matrix rotation will help someone else get it faster in the future.

Using linq to group and sum Listint (Zip?)

You can use this

if StatutOperations is a list of int).

Use this at last line.
StatutOperations= cl.Aggregate((opl1, opl2) =>
{ return opl1.StatutOperations.Zip(opl2.StatutOperations, (opin1,opin2)=>opin1+opin2).ToList(); });

in above code Aggregate runs through two elements and aggregate as sum (op1+op2).

Note : Remember use aggregate if and only if list contains more than one element
.
Edit:
Sorry the above code is incorrect as this is applying aggregate on repere type object and hence the expected return value would be of Repere type.

Edited my code now it should work fine now.

liste_rep.GroupBy(l => l.Nom)
.Select(cl => new Repere
{
Quantite = cl.Sum(c => c.Quantite),
IdAff = cl.First().IdAff,
ID = 0,
ListeOperations = cl.First().ListeOperations,
StatutOperations = cl
.Select(x=>x.StatutOperations)
.Aggregate((x,y)=> x.Zip(y,(p,q)=>p+q).ToList());
}).ToList();

python selecting by rotation from multiple lists

You could make a list of lists and pick the item corresponding to each list's position in the list of lists:

[ L[i] for i,L in enumerate([file1,file2,file3,file4]) ]

[0, 4, 9, 14]

Multiple cycles: (full rotations)

If you have longer lists you can place the list of the lists in a variable, and have it go more than one cycle by multiplying the list of lists by the number of cycles (full rotations):

file1 = list(range(0, 14))
file2 = list(range(3, 17))
file3 = list(range(7, 21))
file4 = list(range(11, 25))

files = [file1,file2,file3,file4]
cycles = min(map(len,files))//len(files)
r = [ L[i] for i,L in enumerate(files*cycles) ]

print(r)
[0, 4, 9, 14, 4, 8, 13, 18, 8, 12, 17, 22]

As a set:

r = { L[i] for i,L in enumerate(files*rotations) }
print(r)
{0, 4, 8, 9, 12, 13, 14, 17, 18, 22}

Multiple rotations (including partial rotations)

If you need to allow for partial rotations at the end of the sequence, you can use modulo indexing over the tuples produced by zip():

files = [file1,file2,file3,file4]
r = [T[i%len(files)] for i,T in enumerate(zip(*files))]

print(r)
[0, 4, 9, 14, 4, 8, 13, 18, 8, 12, 17, 22, 12, 16]

This picks up two more items from file1 and file2:

                                                                  partial
|--------|
file1: [0] 1 2 3 [4] 5 6 7 [8] 9 10 11 [12] 13
file2: 3 [4] 5 6 7 [8] 9 10 11 [12] 13 14 15 [16]
file3: 7 8 [9] 10 11 12 [13] 14 15 16 [17] 18 19 20
file4: 11 12 13 [14] 15 16 17 [18] 19 20 21 [22] 23 24

As a set:

r = {T[i%len(files)] for i,T in enumerate(zip(*files))}

print(r)
{0, 4, 8, 9, 12, 13, 14, 16, 17, 18, 22}

This zip() approach would also work if your input is composed of iterators (e.g. actual files) as it doesn't use any indexing on the data itself. It also makes it easy to offset the rotations using the second parameter of enumerate().

A more "Pythonic" function:

Your complete function could look more "Pythonic" like this:

def get_rotated(*files):
return { T[i%len(files)] for i,T in enumerate(zip(*files)) }

Since your output is going to be a set, you can streamline this using a nested comprehension that combines a striding subscript of each list offset by 1 per list (given that the order of the values won't matter):

def get_rotated(*files):
return {n for i,f in enumerate(files) for n in f[i::len(files)]}

or (for only 4 lists):

def get_rotated(file1,file2,file3,file4):
return {*file1[::4], *file2[1::4], *file3[2::4], *file4[3::4]}


Related Topics



Leave a reply



Submit