104

I'm a little surprised that MATLAB doesn't have a Map function, so I hacked one together myself since it's something I can't live without. Is there a better version out there? Is there a somewhat-standard functional programming library for MATLAB out there that I'm missing?

function results = map(f,list)
% why doesn't MATLAB have a Map function?
results = zeros(1,length(list));
for k = 1:length(list)
    results(1,k) = f(list(k));
end

end

usage would be e.g.

map( @(x)x^2,1:10)
Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 13
    Lesson #1 going from other languages to Matlab: Don't use for loops, they are a few orders of magnitude slower than a vectorized solution. – CookieOfFortune Jun 11 '09 at 19:58
  • 16
    With the introduction of the JIT, for loops do not take the penalty that they once did. – MatlabDoug Jun 12 '09 at 16:31
  • @CookieOfFortune I think that's not true anymore... – Ander Biguri May 28 '13 at 12:54
  • 2
    @AnderBiguri I think they've added some improvements but it's still much slower. – CookieOfFortune May 28 '13 at 18:17
  • The [Functional Library](http://www.mathworks.com/matlabcentral/fileexchange/18835-functional-library) on File Exchange has `map`, `foldl` (also known as `reduce`), `select` (aka `filter`), and other indispensable goodies. Recommended (if you have to use Matlab). – Ahmed Fasih May 21 '15 at 04:36

7 Answers7

138

The short answer: the built-in function arrayfun does exactly what your map function does for numeric arrays:

>> y = arrayfun(@(x) x^2, 1:10)
y =

     1     4     9    16    25    36    49    64    81   100

There are two other built-in functions that behave similarly: cellfun (which operates on elements of cell arrays) and structfun (which operates on each field of a structure).

However, these functions are often not necessary if you take advantage of vectorization, specifically using element-wise arithmetic operators. For the example you gave, a vectorized solution would be:

>> x = 1:10;
>> y = x.^2
y =

     1     4     9    16    25    36    49    64    81   100

Some operations will automatically operate across elements (like adding a scalar value to a vector) while others operators have a special syntax for element-wise operation (denoted by a . before the operator). Many built-in functions in MATLAB are designed to operate on vector and matrix arguments using element-wise operations (often applied to a given dimension, such as sum and mean for example), and thus don't require map functions.

To summarize, here are some different ways to square each element in an array:

x = 1:10;       % Sample array
f = @(x) x.^2;  % Anonymous function that squares each element of its input

% Option #1:
y = x.^2;  % Use the element-wise power operator

% Option #2:
y = f(x);  % Pass a vector to f

% Option #3:
y = arrayfun(f, x);  % Pass each element to f separately

Of course, for such a simple operation, option #1 is the most sensible (and efficient) choice.

gnovice
  • 125,304
  • 15
  • 256
  • 359
10

In addition to vector and element-wise operations, there's also cellfun for mapping functions over cell arrays. For example:

cellfun(@upper, {'a', 'b', 'c'}, 'UniformOutput',false)
ans = 
    'A'    'B'    'C'

If 'UniformOutput' is true (or not provided), it will attempt to concatenate the results according to the dimensions of the cell array, so

cellfun(@upper, {'a', 'b', 'c'})
ans =
ABC
kenm
  • 23,127
  • 2
  • 43
  • 62
2

A rather simple solution, using Matlab's vectorization would be:

a = [ 10 20 30 40 50 ]; % the array with the original values
b = [ 10 8 6 4 2 ]; % the mapping array
c = zeros( 1, 10 ); % your target array

Now, typing

c( b ) = a

returns

c = 0    50     0    40     0    30     0    20     0    10

c( b ) is a reference to a vector of size 5 with the elements of c at the indices given by b. Now if you assing values to this reference vector, the original values in c are overwritten, since c( b ) contains references to the values in c and no copies.

doc
  • 29
  • 1
1

It seems that the built-in arrayfun doesn't work if the result needed is an array of function: eg: map(@(x)[x x^2 x^3],1:10)

slight mods below make this work better:

function results = map(f,list)
% why doesn't MATLAB have a Map function?
for k = 1:length(list)
    if (k==1)
        r1=f(list(k));
        results = zeros(length(r1),length(list));
        results(:,k)=r1;
    else
        results(:,k) = f(list(k));

    end;
end;
end
Foo Bara
  • 11
  • 1
  • 6
    [ARRAYFUN](http://www.mathworks.com/help/techdoc/ref/arrayfun.html) would work for your example, you would just have to include the input arguments `..., 'UniformOutput', false);` to create a cell array output containing your arrays, then format and combine them however you want into a non-cell array. – gnovice Apr 26 '12 at 16:18
0

If matlab does not have a built in map function, it could be because of efficiency considerations. In your implementation you are using a loop to iterate over the elements of the list, which is generally frowned upon in the matlab world. Most built-in matlab functions are "vectorized", i. e. it is more efficient to call a function on an entire array, than to iterate over it yourself and call the function for each element.

In other words, this


a = 1:10;
a.^2

is much faster than this


a = 1:10;
map(@(x)x^2, a)

assuming your definition of map.

Dima
  • 38,860
  • 14
  • 75
  • 115
  • 2
    I think his point was not that he wanted it necessarily to loop, but simply to be specified as having as its result the array of results of applying the supplied function to corresponding elements of the supplied array. I don't know much matlab, but it seems that arrayfun does the job. –  Dec 21 '10 at 21:51
  • 1
    Most built-in Matlab functions and operators already do that: they operate on each element of the input array, and they return a corresponding array of results. – Dima Dec 22 '10 at 15:21
0

You don't need map since a scalar-function that is applied to a list of values is applied to each of the values and hence works similar to map. Just try

l = 1:10
f = @(x) x + 1

f(l)

In your particular case, you could even write

l.^2
Dario
  • 48,658
  • 8
  • 97
  • 130
  • 9
    -1: That's actually not true. Matlab does not have a type system strong enough to specify scalar functions. f is called with the vector and a single vector addition is performed in your example. To verify this, profile your code sample ("profile on" before running the code, then "profile off report" after it). You'll see there's a single call to f. – Mr Fooz Jun 14 '09 at 03:16
-1

Vectorizing the solution as described in the previous answers is the probably the best solution for speed. Vectorizing is also very Matlaby and feels good.

With that said Matlab does now have a Map container class.

See http://www.mathworks.com/help/matlab/map-containers.html

TallBrianL
  • 1,230
  • 1
  • 9
  • 14