Search code examples
matlabcell-array

Fill cell array with elements based on the indices MATLAB


I have a n*m cell array Cell_In:

a b
* *
* *
c *
* d
* *
* f

* --> represents empty string (''). Here is what I need:

a b
a b
a b
c b
c d
c d
c f

For a particular column I need to fill the empty cell with the previous non-empty cell until another non-empty cell is found. Following is the code what I wrote.

b = ~cellfun(@isempty,a);
c = [find(b(:,1) == 1);size(a,1)+1]; e = diff(c);
d = [find(b(:,2) == 1);size(a,1)+1]; f = diff(d);
s1 = ''; s2 = '';
for i = 1:length(e)
    s1 = [s1,repmat(a(c(i),1),1,e(i))];
end

for i = 1:length(f)
    s2 = [s2,repmat(a(d(i),2),1,f(i))];
end
Cell_Out = [s1',s2'];

It's working fine but I want to know the best solution?


Solution

  • Assuming A to be the input cell array, there could be two approaches here.

    Approach #1

    %// Initlialize output array
    Aout = cell(size(A));
    
    for k = 1:size(A,2)
    
        %// Select one column
        Ak = A(:,k);
    
        %// Logical array with size of Ak and ones at places with non-empty strings
        pos = cellfun(@(x) ~isempty(x), Ak);
    
        %// Find unique strings and find indices for all places in that column
        %// with respect to those unique strings
        [unq_str,~,str_idx] = unique(Ak,'stable');
    
        %// Perform cumsum on pos to get an array with a "stepped" array that
        %// steps up at each non-empty string position.
        %// Then replace each stepping number with the string IDs
        idx = changem(cumsum(pos),str_idx(pos),1:sum(pos));
    
        %// Index into each column with those replaced IDs for the final output
        Aout(:,k) = unq_str(idx);
    end
    

    With the input slightly changed for testing out the solution code a bit more aggressively, we had after code run -

    A = 
        'a'    'b'
        ''     '' 
        ''     'a'
        'c'    '' 
        ''     'd'
        'a'    '' 
        ''     'f'
        'c'    'a'
    
    Aout = 
        'a'    'b'
        'a'    'b'
        'a'    'a'
        'c'    'a'
        'c'    'd'
        'a'    'd'
        'a'    'f'
        'c'    'a'
    

    Approach #2 [Compact and maybe more efficient]

    You can reshape the input cell array into a single columned cell array and as such you won't need to loop through the columns of the cell array and this could lead to a more efficient and compact code -

    %// Reshape all cells into a single columned cell array
    A1 = A(:);
    
    %// Rest of the code borrowed from previous approach with reshaping added
    %// at the end to bring the output back to the size of input array
    pos = ~cellfun('isempty', A1);
    [unq_str,~,str_idx] = unique(A1,'stable');
    Aout =  reshape(unq_str(changem(cumsum(pos),str_idx(pos),1:sum(pos))),size(A));
    

    Bonus: Customized implementation of changem

    The codes listed earlier uses changem that needs Mapping Toolbox. So, if you do have it, here's a customized version of it, implemented with bsxfun and max and is merely a polished version of an earlier solution code posted here. Here goes the custom function code -

    %// CHANGEM_CUSTOM  Home-cooked vesion of CHANGEM with MAX, BSXFUN
    function A  = changem_custom(A,newvals,oldvals)
    
    [valid,id] = max(bsxfun(@eq,A(:),oldvals(:).'),[],2); %//'
    A(valid) = newvals(id(valid));
    
    return;
    

    So, to use this custom function to replace changem, just replace the function call name there in the earlier codes.