Lucas
Lucas

Reputation: 257

Create a MATLAB dictionary like in Python

I would like to know if there exists a way in MATLAB to create a dictionary like in Python.

I have several Port Name and Port Type and I would like to create a dictionary like this :

dict = {PortName : PortType, PortName : PortType, ...}

Upvotes: 3

Views: 8097

Answers (4)

Luis Mendo
Luis Mendo

Reputation: 112749

EDIT: Since R2022b Matlab has a dictionary type, which is recommended over containers.Map.


The closest analogy is containers.Map:

containers.Map: Object that maps values to unique keys

The keys are character vectors, strings, or numbers. The values can have arbitrary types.

To create the map you pass containers.Map a cell array of keys and a cell array of values (there are other, optional input arguments):

>> dict = containers.Map({ 'a' 'bb' 'ccc' }, { [1 2 3 4], 'Hey', {2 3; 4 5} });
>> dict('a')
ans =
     1     2     3     4
>> dict('bb')
ans =
    'Hey'
>> dict('ccc')
ans =
  2×2 cell array
    {[2]}    {[3]}
    {[4]}    {[5]}

You can also append key-value pairs to an existing map:

>> dict('dddd') = eye(3);
>> dict('dddd')
ans =
     1     0     0
     0     1     0
     0     0     1

However, depending on what you want to do there are probably more Matlab-like ways to do it. Maps are not so widely used in Matlab as dictionaries are in Python.

Upvotes: 8

WalkingRandomly
WalkingRandomly

Reputation: 4557

MATLAB R2022b has a new dictionary datatype that is significantly better than containers.Map. A tutorial describing it can be found on The MATLAB blog https://blogs.mathworks.com/matlab/2022/09/15/an-introduction-to-dictionaries-associative-arrays-in-matlab/

For example:

names = ["Mike","Dave","Bob"];
weights = [89,75,68]; % weight in kilograms
gym = dictionary(names,weights) % Vectorised constructor. Performs elementwise mapping.

gives

gym = 

      dictionary (string ⟼ double) with 3 entries:
    
        "Mike" ⟼ 89
        "Dave" ⟼ 75
        "Bob"  ⟼ 68

Upvotes: 6

OverLordGoldDragon
OverLordGoldDragon

Reputation: 19806

I wrote a func to make containers.Map via dict(a=1, b='yes') syntax. Dunno if foulproof but seems to work, also wrote a function to pretty print a containers.Map. Demo:

d = dict(a=1, mode='fast', arr=[[1 2 3]; [5 6 7]], yes=dict(k=2));
disp(dict2str(d))
a=1, arr=[1 2 3] [5 6 7], mode='fast', yes=dict(k=2,),

also guards against very long prints but this can be improved:

d = dict(a=randn(1000));
disp(dict2str(d))
a=<double>,

function out = dict(varargin)
    % DICT Python dictionary syntax
    %
    %    out = dict(a=1, mode='fast', arr=[1 2 3], yes=dict(k=2))
    %
    % Show with
    %
    %    disp(dict2str(out))
    out = containers.Map('UniformValues', false);
    current_key = '';
    for i=1:length(varargin)
        if mod(i, 2)
            current_key = varargin{i};
        else
            out(current_key) = varargin{i};
        end
    end
end


function out = dict2str(d, C)
    % DICT2STR
    %
    %    `disp(out)` is like
    %        analytic=true, normalize='l1', paths_exclude=None, r_psi=.70711
    arguments
        d;
        C.delim = ', ';
        C.max_value_length = 50;
    end
    keys = d.keys;
    values = d.values;

    out = "";
    for i=1:length(keys)
        k = keys{i};
        v = values{i};
        if class(v) == "containers.Map"
            vs = "dict(" + dict2str(v) + ")";
        elseif isnumeric(v) && length(v) > 1
            vs = "[" + join(string(v)) + "]";
        elseif isempty(v)
            vs = "None";
        elseif ismember(class(v), ["string", "char"])
            vs = sprintf("'%s'", join(string(v)));
        else
            vs = join(string(v));
        end
        if C.max_value_length && length(vs) > C.max_value_length
            vs = "<" + class(v) + ">";
        end
        out = out + sprintf("%s=%s%s", join(string(k)), join(string(vs)), C.delim);
    end

    % strip delimiter
    for i=1:length(C.delim)
        out = strip(out, 'right', C.delim(i));
    end
end

Upvotes: 0

Cris Luengo
Cris Luengo

Reputation: 60695

You can use containers.Map as suggested by Luis Mendo in the other answer, but I think in this case a struct is much simpler:

>> dict = struct('a',[1 2 3 4], 'bb','Hey', 'ccc',{2 3; 4 5});
>> dict.('a')
ans =
     1     2     3     4
>> dict.a
ans =
     1     2     3     4
>> dict.b
ans =
    'Hey'
>> dict.ccc
ans =
  2×2 cell array
    {[2]}    {[3]}
    {[4]}    {[5]}

>> dict.dddd = eye(3);
>> dict.('eee') = eye(3);
>> dict.dddd
ans =
     1     0     0
     0     1     0
     0     0     1

That is, the struct is always indexed using .('name') or simply .name. But there are restrictions on what 'name' can be (it must be a valid variable name), unlike for the Map.

See this other answer to learn about important differences between the two approaches.

Upvotes: 5

Related Questions