How do I use containers.Map in Matlab with a cell array as the keys and a vector of integers as the values - matlab

I have a cell array that contains words like 'a', 'b', and 'c' for example. What I want to be able to do is use Matlab's containers.Map to make a hash table that I can add values to for each key and be able to look them up quickly. I am able to do this if I do not initialize my containers.Map object beforehand as follows but it does not allow me (or at least I haven't found a way) to add more key/value pairs later and makes it so that I have to reinitialize the object during each iteration of a loop:
key = {'a','b','c'};
newmap = containers.Map(key,[1,2,3]);
My problem is that I need to be able to continually add new keys to the hash table and therefore cannot keep initializing the containers.Map object each time, I want one long hash table with all the keys and values that I get while in a loop.
Here is the code that I am trying to get working, I want to be able to add the keys to the containers.Map object newmap and their corresponding values at the same time. The keys are always strings in a cell array and the values are always integers:
key = {'a','b','c'};
val = [1,2,3];
newmap = containers.Map(); % containers.Map object initialization
newmap(key) = val;
My desired output would something like this:
newmap(key)
ans = 1 2 3
Attempts at solving this:
I have tried converting the cell array of keys using cellstr() and char() but haven't had any luck with these. I seem to keep getting this error when trying this:
Error using containers.Map/subsref
Specified key type does not match the type expected for this container.
Thanks for any help.

% Initialize
map = containers.Map('KeyType','char','ValueType','double');
% Assume you get these incrementally
key = {'a','b','c'};
val = [1,2,3];
% Add incrementally
for ii = 1:numel(key)
map(key{ii}) = val(ii);
end
You can retrieve all values at once for a given set of keys, but you will get a cell array. No way around it, but you can convert with cell2mat(), or retrieve incrementally with a loop map(key{ii})
% Retrieve all
values(map,key)
ans =
[1] [2] [3]

Related

MATLAB: removing non-unique numbers from a field within a structure

I have a structure in Matlab, each field contains elements with varying numbers of variables. I would like to remove duplicates of numbers that appear within the same field: I know the unique() function and know how to use it to scan through fields one at a time but not an entire field.
I think I want something like:
structure(1:length(structure)).field=unique(structure(1:length(structure)).field
and get
original
field=[1,2,3] [1,4,5] [2,5,8]
to turn into
field=[1,2,3] [4,5] [8]
Maybe a complicated for loop similar to below (isn't working) which would grab the values from the first element in the field, search through each additional element, and if that value is present set it equal to =[];, and iterate through that way?
for n=1:length(RESULTS)
for m=1:length(RESULTS(n).Volumes)
for l=1:length(RESULTS)
for o=1:length(RESULTS(l).Volumes)
if RESULTS(n).Volumes(m)==RESULTS(l).Volumes(o)
RESULTS(l).Volumes(o)=[];
end
end
end
end
end
Thanks!
This is a quick-and-dirty attempt, but you might be able to improve on it. Assume your struct array and field are sa(:).v. I'm also assuming that the field contains a 1xn array of numbers, as in your example. First, make a "joint" array with the concatenation of all field values and filter the non-unique values:
joint = cell2mat({sa.v});
[uniqJoint,~,backIdx] = unique(joint);
The "uniqJoint" array has also been sorted, but the "backIdx" array contains the indices that, if applied to uniqJoint, will rebuild the original "joint" array. We need to somehow connect those to the original indices (i,j) into the struct array and within the field value sa(i).v(j). To do that, I tried creating an array of the same size as "joint" that contains the indices of the struct that originally had the corresponding element in the "joint" array:
saIdx = cell2mat(arrayfun(#(i) i * ones(1,length(sa(i).v)), ...
1:length(sa), 'UniformOutput', false));
Then you can edit (or, in my case, copy and modify the copy of) the struct array to set the field to the values that have not appeared before. To do that, I keep a logical array to mark the indices of "backIdx" as "already used", in which case I skip those values when rebuilding the fields of each struct:
sb = sa;
used = false(length(backIdx));
for i = 1:length(sa)
origInd = find(saIdx == i); % Which indices into backIdx correspond to this struct?
newInd = []; % Which indices will be used?
for curI = backIdx(origInd)
if ~used(curI)
% Mark as used and add to the "to copy" list
used(curI) = true;
newInd(end+1) = curI;
end
end
% Rewrite the field with only the indices that were not used before
sb(i).v = uniqJoint(newInd);
end
In the end, the data in sb(i).v contains the same numbers as sa(i).v without repeats, and removing those that appeared in any previous elements of the struct.

Save a string, double and table Matlab

I have a loop which runs 100 times. In each iteration there is a string, double and a table assigned, and in the next iteration new values are assigned for them. What I want to do is to accumulate these values and after the loop finishes save the total result as result.mat using the matlab save function. I've tried putting them in cell-array but its not working so far, so if anyone could please advise how this can be done.
This is what I did:
results_cell=(100,3);
.
.
.
results_cell(i,1)=stringA;
results_cell(i,2)=TableA;
results_cell(i,3)=DoubleA;
But it gives this error Coversion to Cell from Table is not possible. So I've tried converting TableA to array of Doubles using table2array but I still get this Coversion to Cell from Double is not possible
I think using a structure would be a good way to store your data, since they are of different types and you can assign it meaningful field names for easy reference.
For example, let's call the structure Results. You can initialize it like so.
Results = struct('StringData',[],'TableData',[],'DoubleData',[])
Since you know its dimensions, you can even do this:
N = 100;
Results(N).StringData = [];
Results(N).TableData = [];
Results(N).DoubleData = [];
This automatically create a 1xN structure with 3 fields.
Then in your loop you can assign each field with its associated data like so:
for k = 1:N
Results(k).StringData = String(k);
Results(k).TableData = Table(k);
Results(k).DoubleData = Double(k);
end
where String(k), Table(k) and Double(k) are just generic names for your actual data.
When you're done with the loop you can access any type of data using a single index and the right field name.
In order to save a .mat file, use something like this:
save SomeFileName.mat Results
Which you can load into the workspace as you would with any .mat file:
Eg:
S = load('SomeFileName.mat')
R = S.Results
Hope that helps!

Give value, return field name in matlab structure

I have a Matlab structure like this:
Columns.T21=6;
Columns.ws21=9;
Columns.wd21=10;
Columns.u21=11;
Is there some elegant way I can give the value and return the field name? For instance, if I give 6 and it would return 'T21.' I know that fieldnames() will return all the field names, but I want the fieldname for a specific value. Many thanks!
Assuming that the structure contains fields with scalar numeric values, you can use this struct2array based approach -
search_num = 6; %// Edit this for a different search number
fns=fieldnames(Columns) %// Get field names
out = fns(struct2array(Columns)==search_num) %// Logically index into names to find
%// the one that matches our search
Goal:
Construct two vectors from your struct, one for the names of fields and the other for their respective values. This has analogy to the dict in Python or map in C++, where you have unique keys being mapped to possibly non-unique values.
Simple Solution:
You can do this very simply using the various functions defined for struct in Matlab, namely: struc2cell() and cell2mat()
For the particular element of interest, say 1 of your struct Columns, get the names of all fields in the form of a cell array, using fieldnames() function:
fields = fieldnames( Columns(1) )
Similarly, get the values of all the fields of that element of Columns, in the form of a matrix
vals = cell2mat( struct2cell( Columns(1) ) )
Next, find the field with the corresponding value, say 6 here, using the find function and convert the resulting 1x1 cell into a char using cell2mat() function :
cell2mat( fields( find( vals == 6 ) ) )
which will yield:
T21
Now, you can define a function that does this for you, e.g.:
function fieldname = getFieldForValue( myStruct, value)
Advanced Solution using Map Container Data Abstraction:
You can also choose to define an object of the containers.map class using the field-names of your struct as the keySet and values as valueSet.
myMap = containers.Map( fieldnames( Columns(1) ), struct2cell( Columns(1) ) );
This allows you to get keys and values using corresponding built-in functions:
myMapKeys = keys(myMap);
myMapValues = values(myMap);
Now, you can find all the keys corresponding to a particular value, say 6 in this case:
cell2mat( myMapKeys( find( myMapValues == 6) )' )
which again yields:
T21
Caution: This method, or for that matter all methods for doing so, will only work if all the fields have the values of the same type, because the matrix to which we are converting vals to, need to have a uniform type for all its elements. But I assume from your example that this would always be the case.
Customized function/ logic:
struct consists of elements that contain fields which have values, all in that order. An element is thus a key for which field is a value. The essence of "lookup" is to find values (which are non-unique) for specific keys (which are unique). Thus, Matlab has a built-in way of doing so. But what you want is the other way around, i.e. to find keys for specific values. Since its not a typical use case, you need to write up your own logic or function for it.
Suppose your structure is called S. First extract all the field names into an array:
fNames=fieldnames(S);
Now define a following anonymous function in your code:
myfun=#(yourArray,desiredValue) yourArray==desiredValue;
Then you can get the desired field name as:
desiredFieldIndex=myfun(structfun(#(x) x,S),3) %desired value is 3 (say)
desiredFieldName=fNames(desiredFieldIndex)
Alternative using containers.Map
Assuming each field in the structure contains one scalar value as in the question (not an array).
Aim is to create a Map object with the field values as keys and the field names as values
myMap = containers.Map(struct2cell(Columns),fieldnames(Columns))
Now to get the fieldname for a value index into myMap with the value
myMap(6)
ans =
T21
This has the advantage that if the structure doesn't change you can repeatedly use myMap to find other value-field name pairs

Matlab map with multiple keys or nested

I am currently trying to map some data within a month depending on 'Month', 'week', 'day'.
This means that in principle I have three keys. I thought of following solutions to the problem:
1)
containers.Map(month,containers.Map(week,containers.Map(day,value)))
The problem with this solutions is to reference it I must use: How can I index a MATLAB array returned by a function without first assigning it to a local variable?.
And I find this solution rather "ugly".
2)
So another way would be to use a Map with multiple keys. The containers documentation show that only a single dimension key is allowed.
Question:
Do you have any trick to solve this multiple key problem?
Update/Solution:
I ended using a concatenated string as a key (as suggested) I added following piece of code to make the containers.Map as a proper multi-dimensional hashmap. Take a look below (I excluded week for simplification):
classdef example
properties
myMap % Map for storage
end
methods
function obj = example()
obj.myMap = containers.Map;
end
function obj2 = setVal(obj2,value,Month,DayType)
key = strcat(num2str(Month),'-',num2str(DayType));
obj2.myMap(key) = value;
end
function value = getValue(obj,Month,DayType)
key = strcat(num2str(Month),'-',num2str(DayType));
value = obj.myMap(key);
end
end
You could perhaps use all three of those keys to build a single key. I'm assuming this can be done as Month, week and day could be considered as unique. There is only one unique combination of these per occurrence. As such, simply take these keys and build them into single strings, then use theses as keys into your dictionary / containers.Map().
Here's an example:
%// Test data
month1 = 'May';
week1 = 2;
day1 = 'Thursday';
month2 = 'June';
week2 = 3;
day2 = 'Friday';
month3 = 'July';
week3 = 4;
day3 = 'Sunday';
%// Define keys
key1 = [month1 num2str(week1) day1];
key2 = [month2 num2str(week2) day2];
key3 = [month3 num2str(week3) day3];
%// Build dictionary
M = containers.Map();
M(key1) = 'Hello!';
M(key2) = 'Testing!';
M(key3) = 'Yes!';
%// Now test accessing
disp(M(key1));
disp(M(key2));
disp(M(key3));
The above code will take three months, three weeks and days, convert them into strings, and use these as keys into your dictionary. I don't know what the output type is for your purposes, so I just assigned strings. Note that I took the numbers and used num2str to convert the numbers into strings to ensure compatibility with the rest of the string. I don't know what data type week is (or any of the other variables in fact...), so simply use what I have and modify it for your own purposes.
I create the dictionary, then to test it, I access each of the values with each of the keys. As expected, my output is:
Hello!
Testing!
Yes!
There is a new MapNested class on Matlabs File-exchange:
http://de.mathworks.com/matlabcentral/fileexchange/62492-mapnested-implementation-for-nested-maps--map-of-maps-
or on github:
https://github.com/RolandRitt/Matlab-NestedMap
Check it out!
The syntax for setting an value is the following:
NMapobj = MapNested(); %constructor;
NMapobj(key1, key2, key3) = value;
for retrieving:
value = NMapobj(key1, key2, key3);

assigning values to a field of an structure array in MATLAB

I want to replace the value of the fields in a structure array. For example, I want to replace all 1's with 3's in the following construction.
a(1).b = 1;
a(2).b = 2;
a(3).b = 1;
a([a.b] == 1).b = 3; % This doesn't work and spits out:
% "Insufficient outputs from right hand side to satisfy comma separated
% list expansion on left hand side. Missing [] are the most likely cause."
Is there an easy syntax for this? I want to avoid ugly for loops for such simple operation.
Credits go to #Slayton, but you actually can do the same thing for assigning values too, using deal:
[a([a.b]==1).b]=deal(3)
So breakdown:
[a.b]
retrieves all b fields of the array a and puts this comma-separated-list in an array.
a([a.b]==1)
uses logical indexing to index only the elements of a that satisfy the constraint. Subsequently the full command above assigns the value 3 to all elements of the resulting comma-separated-list according to this.
You can retrieve that the value of a field for each struct in an array using cell notation.
bVals = {a.b};
bVals = cell2mat( bVals );
AFAIK, you can't do the same thing for inserting values into an array of structs. You'll have to use a loop.