I have an array of "object" structures, OBJECT_ARRAY, that I must often transform into individual arrays for each element of the object structures. This can be done using arrayfun. It's more tedious than simply refereeing to OBJECT_ARRAY(k).item1, but that's how The Mathworks chose to do it.
In this case today, I have used those individual arrays and calculated a corresponding derived value, newItem, for each element and I need to add this to the original array of structures. So I have an array of newItems.
Is there a straightforward way to do an assignment for each object in OBJECT_ARRAY so that (effectively) OBJECT_ARRAY(k).newItem = newItems(k) for every index k?
I am using version 2015a.
You shouldn't need arrayfun for any of this.
To get values out, you can simply rely on the fact that the dot indexing of a non-scalar struct or object yields a comma-separated list. If we surround it with [] it will horizontally concatenate all of the values into an array.
array_of_values = [OBJECT_ARRAY.item1];
Or if they are all different sizes that can't be concatenated, use a cell array
array_of_values = {OBJECT_ARRAY.item1};
To do assignment, you can again use the comma separated list on the left and right side of the assignment. We first stick the new values in a cell array so that we can automatically convert them to a comma-separated list using {:}.
items = num2cell(newitems);
[OBJECT_ARRAY.item1] = items{:};
Related
I've just started with MATLAB, having mainly played around with Python previously. I've just made my first associative array, and am a little confused with how it's dealing with commas and spaces. My array is:
co_comma=containers.Map({'Open University','UCL',' University of Edinburgh','Birkbeck'},{193835,21210,24525,17822})
I also made a second associative array, splitting using spaces:
co_space=containers.Map({'Open University' 'UCL' ' University of Edinburgh' 'Birkbeck'},{193835 21210 24525 17822})
They both give the following:
Map with properties:
Count: 4
KeyType: char
ValueType: double
But co_comma==co_space gives False:
ans =
logical
0
Questions:
how are these associative arrays different
what actually is a container? Although I've never thought of lists etc in this way, Python seems to have containers in the form of lists/tuples/general iterables - https://stackoverflow.com/a/11576019/11357695. So is a matlab string container vs a matlab char array different in the same way as (for example) python lists and python tuples are different?
Thanks :D
Many things mixed in here!
Clarifications:
A matlab container is only equivalent to a python dictionary, not a list/tuple/general iterable.
Both of your containers are created the same. You seem to be naming them comma and space, but this distinction does not even reach the definition of the container.
Both {'Open University','UCL',' University of Edinburgh','Birkbeck'} and {'Open University' 'UCL' ' University of Edinburgh' 'Birkbeck'} create the exact same cell array, so the input to container.map is the same. you are comparing a=[5]; and b=5, as a MATLAB-valid analogy. They are the same.
For objects, most programing languages (including python!) will give you false when you compare two objects that contain the same values, yet are different objects. Only basic variables tend to compare by value, and not by some sort of objecID. In your case, simply doing isequal(co_comma,co_space) will return true, so their values are the same (as we already know, from the previous point)
Containers are not generally used in MATLAB, unless you specifically want a dictionary.
So here's the deal with Matlab: Matlab is an oddball. In Matlab, everything is an array, and there is no real distinction between regular "values" and "containers" or "collections" like there is in many programming languages. In Matlab, any numeric value is a numeric array. Any string value is actually an array of strings. Every value is iterable and can be used in a for loop or other "list"-like context. And every type or class in Matlab must implement collection/container behaviors as well as its "plain" value semantics.
Scalar values in Matlab are actually degenerate cases of two-dimensional arrays that are 1-by-1 in size. The number 42? Actually a 1-long array of doubles. The string "foo"? Actually a 1-long array of strings. Everything in Matlab is actually like a list in Python (or, more accurately, a NumPy series or array).
A cell array is an array of things, which can contain any other type of array in each of its elements. This is used for heterogeneous collections.
A char array is a bit weird, because it is used to represent strings, but its elements are not themselves strings, but rather characters. Plain char arrays are used in a weird way to represent lists of strings: you make a 2-D char array, and read each row as a string that is padded with spaces on the right. Lists of strings that are different lengths used to be represented as "cellstrs", which are cell arrays that contain char row vectors in each element. It's weird; see my blog post about it. The new string array makes most uses of other string types obsolete. You might want to use strings here. In Matlab, string literals are double-quoted, and char literals are single-quoted.
The word "container" doesn't have a specific technical meaning in Matlab, and is not really a thing. There's the containers package, but the only thing in it is containers.Map; there are no other types of containers and no generalization of what it means to be a container. One of the Matlab developers had an idea for making containers like this, but it never really went anywhere. And as far as I can tell, containers.Map hardly gets used at all: containers.Map is a pass-by-reference "handle" object, whereas most Matlab types are pass-by-value, so Map can't be used easily with most Matlab code.
So, putting aside the weirdness of chars, everything in Matlab has array semantics, and is effectively an iterable container like Python lists or tuples. And in Matlab, most values and objects are immutable and pass-by-value, so they are more like Python tuples than Python lists.
Growing an array, e.g. through x = [x, a] in a loop, is usually frowned upon in Matlab programming, because it leads to one or more resize operations, and therefore preallocation is usually the better option. However, in some contexts, for example as a reduction assignment in a parfor block, it can have advantages. For that purpose, the array has to be created as zero-size. For numerical arrays, the expression for that is [], for cell arrays {}.
For struct arrays, it is not so clear cut. For example initialization as struct() does not create a zero-size struct array, but a 1x1 struct array with no fields. This leads to the error
Error using horzcat
Number of fields in structure arrays being concatenated do not match.
Concatenation of structure arrays requires that these arrays have the same set of fields.
while growing, because the field structure of appended structs is incompatible with the "no fields" struct.
How to initialize a zero-size struct array for growing?
Growing an array by concatenation
x = [x, 0];
is very slow, see here and here. Instead, one should (if pre-allocation is not possible for whatever reason), grow an array by appending a new element like so:
x(end+1) = 0;
The reason is that [x,0] creates a new array, copying the old data into it, at every single loop iteration, whereas the other form extends the array, needing reallocation only occasionally (it doubles the underlying memory size when the array becomes too small).
To initialize an empty struct array (as suggested by #LuisMendo in a comment) one can do this:
s = struct('field1', {}, 'field2', {});
To append to it, do:
s(end+1) = struct('field1', 1, 'field2', 'x');
Alternatively, one can append by
s(end+1).field1 = 5;
s(end).field2 = 'y';
Note that in this case, end+1 only happens when first appending a new element to the array, subsequent fields are written to the last array element.
It turns out the answer is to initialize to a zero-size numeric array, x = []. Even though it is not a struct array, appending a struct a via x = [x, a] generates a 1x1 struct array, to which further structs with the same fields can be appended.
This unintuitive behavior seems to be a general quirk of the Matlab language, in that [] is of "flexible type". It is initially a zero-size array of doubles, but appending singles makes it a single array, and appending cell arrays makes it a cell array.
As #LuisMendo pointed out in the comments, struct([]) directly gives a zero-size struct array with no fields. This syntax may be preferred because it is less ambiguous. Moreover, s = struct('field1', {}, 'field2', {}) may be used to create a zero-size struct array with defined fields.
I just found that #CrisLuengo's proposal in the comments, appending by assignment to end + 1 instead of concatenation, does not work in the context of the question, "initialize for growing". Assignment cannot change the "type" of the struct, which is defined by its fields. Changing from no fields to the fields of whatever struct is to be appended leads to the error "Subscripted assignment between dissimilar structures."
(but see his comment and my answer for clarification)
In MATLAB, the i-th element of an array is accessed by a(i), while the i-th element of a cell is accessed by a{i}. So my code has to do different things in terms of whether a is a cell or not.
Is there a better way to do it? so we can access the i-th element in a 'same'? way.
You can access the ith element of any array in the same way, a(i), and the behaviour is completely uniform: you get back an object of the same class as a. So if a is a 5x5 double array, a(i) is a 1x1 double. Similarly, if a is a cell array, then a(i) is a 1x1 cell. So far, so logical and consistent.
Now, if you want to look inside a cell, you use a{i} but that is a fundamentally different operation. Your question seems to imply that it would be "better" if these two fundamentally different operations had the same syntax. It wouldn't: if this were the case, then the ability to slice a cell array into arbitrary shapes (including 1x1) would be overshadowed.
But you can always write a custom function. Among the many Matlab workarounds I carry with me everywhere, I have the following pair of functions:
function a = ascell(a)
if ~iscell(a), a = {a}; end
and
function a = uncell(a)
if iscell(a) & numel(a) == 1, a = a{1}; end
With uncell.m on your path, b = uncell(a(i)); would give you the ith element of a with the cell wrapping, if any, stripped off.
It is good to have the call to uncell visible in the code because it alerts you (or another maintainer) to the possibility that a might legally be a cell or a non-cell—this is by no means necessarily true in everybody's coding strategy. Nor will my code necessarily follow the same convention as yours when it comes to interpreting the meaning and correct treatment of a cell array where a non-cell was expected, or a non-cell where a cell was expected (and this is another way of explaining why there's no common syntax). This leads me to the question: if the design of your application is such that a can by its nature contain elements with mismatched shapes or types, then why not simply decree that it is always a cell, never a non-cell, and always access the ith element as a{i}?
For example
A = {{1,2},{23,34},{45,4},...}
How to create such a data type in MATLAB
How to access the i-th element and the elements within it?
For Ex. A[2] should return {23,34} and A[2].1 should return 23.
A = {{1,2},{23,34},{45,4},...}
is valid MATLAB syntax, if you are trying to make a cell array of cell arrays. However, you'd probably want to store vectors rather than arrays in your array:
A = {[1,2],[23,34],[45,4],...}
Access them like A{2} or A(2) but far more likely the first one. If you want an individual element then A{2}(1)
But if every one of your cells is going to contain a 2 element vector then you will have a much easier time just using a 2D matrix:
A = [1,2;23,34;45,4;,...]
And and now access the entire row i.e. A(2,:) or for an individual element A(2,1)
If a Julia function returns an array, is the reference returned or a copy?
function pass(A::Matrix)
return A
end
A real example is reshape:
reshape(A, dims)
Create an array with the same data as the given array, but with different dimensions. An implementation for a particular type of array may choose whether the data is copied or shared.
How does the implementation determine whether data is copied or shared?
The pass function above returns by reference, http://julia.readthedocs.org/en/latest/manual/arrays/ .
There is a bit more to the reshape example.
For full arrays the reshaped array is a new array object that shares the same data. But keep in mind that there are plenty of specialized array types. The docs warn you not to rely on that because for example for a future implementation of immutable fixed sized arrays a different reshape mechanism could be used.