2

In many other languages, to zip a number of lists together means to take something like

((x1, x2, x3, x4), (y1, y2, y3, y3))

and turn it into

((x1, y1), (x2, y2), (x3, y3), (x4, y4))

This of course generalises to lists

  • with more than 2 sublists,
  • whose sublists are not of length 4, and
  • whose sublists may have entirely different lengths.

I find myself doing this a lot, so I thought I'd share the solution.

Amro
  • 123,847
  • 25
  • 243
  • 454
Derek
  • 647
  • 4
  • 17
  • It's nice to see an interesting Matlab question in SO now and then :-) – Luis Mendo Oct 02 '14 at 21:18
  • @LuisMendo: yep, this reminds me of an old question about doing `cell2mat` with padding: http://stackoverflow.com/questions/3054437/how-can-i-accumulate-cells-of-different-lengths-into-a-matrix-in-matlab – Amro Oct 02 '14 at 22:25

3 Answers3

2

Here is my implementation:

function out = zipCells(varargin)
    % make sure all inputs are cell arrays
    assert(all(cellfun(@iscell, varargin)), 'Expecting cell arrays');

    % make them all of the same length
    mn = min(cellfun(@numel, varargin));
    in = cellfun(@(c) c(1:mn), varargin, 'UniformOutput',false);

    % zip lists
    out = cellfun(@(varargin) varargin, in{:}, 'UniformOutput',false);
end

(The last line is using comma-separated lists to expand the cell array of cell arrays in{:})

Example:

>> c = zipCells({'x','y','z'}, {1,2}, {'a','b','c'})
c = 
    {1x3 cell}    {1x3 cell}
>> c{:}
ans = 
    'x'    [1]    'a'
ans = 
    'y'    [2]    'b'
Amro
  • 123,847
  • 25
  • 243
  • 454
1
  1. If all cells have the same length, there's a simple solution without cellfun, arrayfun or loops. It is based on the fact that mat2cell (inspite of its name) can be applied to a cell array:

    function out = zipCells(varargin)
    n = numel(varargin{1});
    out = mat2cell(reshape([varargin{:}], n, []), ones(1, n)).';
    
  2. In the general case: trim cells to minimum length (two uses of cellfun) and proceed as above:

    function out = zipCells(varargin)
    n = min(cellfun(@numel, varargin));
    varargin = cellfun(@(c) c(1:n), varargin, 'uniformoutput', 0);
    out = mat2cell(reshape([varargin{:}], n, []), ones(1, n)).';
    
  3. An extension to allow input cells with different orientations (row/column):

    function out = zipCells(varargin)
    n = min(cellfun(@numel, varargin));
    varargin = cellfun(@(c) reshape(c(1:n), 1,[]), varargin, 'uniformoutput', 0);
    out = mat2cell(reshape([varargin{:}], n, []), ones(1, n)).';
    
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
  • `num2cell(.,2)` can also be used to simplify the `mat2cell(.,ones(1,n))` call – Amro Oct 02 '14 at 22:22
  • @Amro Strange how these functions' names don't reflect all they can do. I guess their functionality was extended but the name was kept – Luis Mendo Oct 02 '14 at 22:27
0

You can of course chan everything together, but the names should help describe what's happening:

function output = zipCells(varargin)

numComponents = min(cellfun(@length, varargin));
makeComponentI = @(i) cellfun(@(c) c{i}, varargin, 'UniformOutput', false);
output = arrayfun(makeComponentI, 1:numComponents, 'UniformOutput', false);

Here's it in action:

>> pairs = zipCells({'a' 'b' 'c' 'd'}, {1 2 3 4})
pairs = 
    {1x2 cell}    {1x2 cell}    {1x2 cell}    {1x2 cell}
>> pairs{:}
ans = 
    'a'    [1]
ans = 
    'b'    [2]
ans = 
    'c'    [3]
ans = 
    'd'    [4]

Hope this helps.

Derek
  • 647
  • 4
  • 17
  • When the time limit has passed, consider accepting your answer to signal to the StackOverflow community that you no longer need any help. – rayryeng Oct 02 '14 at 18:18