Sorting Structure Arrays based on Fields
Jiro's pick this week is Nested Struct Sort by Jake Hughey.
MATLAB has a nice function sortrows to sort matrices (numeric or cells) based on specific column(s). I use it often when I am doing some data management. If you have Statistics Toolbox, there are corresponding functions for dataset arrays and ordinal arrays.
Sometimes, I want to do a similar action on structure arrays. I want to be able to sort according to specific fields. In order to do that, it requires some data manipulations. Let's take a look at an example.
A = struct(... 'name', {'mike', 'doug', 'steve', 'loren', 'jiro', 'brett', 'seth'}, ... 'year', {2005, 2001, 1993, 1987, 2006, 2005, 1998}, ... 'day', {'Mon', 'Fri', 'Wed', 'Fri', 'Mon', 'Mon', 'Mon'}) for id = 1:length(A) fprintf('%d\n',id) disp(A(id)) end
A = 1×7 struct array with fields: name year day 1 name: 'mike' year: 2005 day: 'Mon' 2 name: 'doug' year: 2001 day: 'Fri' 3 name: 'steve' year: 1993 day: 'Wed' 4 name: 'loren' year: 1987 day: 'Fri' 5 name: 'jiro' year: 2006 day: 'Mon' 6 name: 'brett' year: 2005 day: 'Mon' 7 name: 'seth' year: 1998 day: 'Mon'
Let's sort the array by the field "name". First you need to convert to a cell array:
Afields = fieldnames(A); Acell = struct2cell(A); sz = size(Acell) % Notice that the this is a 3 dimensional array. % For MxN structure array with P fields, the size % of the converted cell array is PxMxN
sz = 3 1 7
Once it's a cell array, you can sort using sortrows:
% Convert to a matrix Acell = reshape(Acell, sz(1), []); % Px(MxN) % Make each field a column Acell = Acell'; % (MxN)xP % Sort by first field "name" Acell = sortrows(Acell, 1)
Acell = 7×3 cell array {'brett'} {[2005]} {'Mon'} {'doug' } {[2001]} {'Fri'} {'jiro' } {[2006]} {'Mon'} {'loren'} {[1987]} {'Fri'} {'mike' } {[2005]} {'Mon'} {'seth' } {[1998]} {'Mon'} {'steve'} {[1993]} {'Wed'}
And convert it back to a structure array:
% Put back into original cell array format Acell = reshape(Acell', sz); % Convert to Struct Asorted = cell2struct(Acell, Afields, 1); for id = 1:length(Asorted) fprintf('%d\n',id) disp(Asorted(id)) end
1 name: 'brett' year: 2005 day: 'Mon' 2 name: 'doug' year: 2001 day: 'Fri' 3 name: 'jiro' year: 2006 day: 'Mon' 4 name: 'loren' year: 1987 day: 'Fri' 5 name: 'mike' year: 2005 day: 'Mon' 6 name: 'seth' year: 1998 day: 'Mon' 7 name: 'steve' year: 1993 day: 'Wed'
Simpler Approach
Do you want an easier way of doing it? Just use Jake's nestedSortStruct:
B = nestedSortStruct(A, 'name');
isequal(B, Asorted)
ans = logical 1
You can even sort by multiple fields. For example, let's sort by "year" and then by "name":
C = nestedSortStruct(A, {'year', 'name'}); for id = 1:length(C) fprintf('%d\n',id) disp(C(id)) end
1 name: 'loren' year: 1987 day: 'Fri' 2 name: 'steve' year: 1993 day: 'Wed' 3 name: 'seth' year: 1998 day: 'Mon' 4 name: 'doug' year: 2001 day: 'Fri' 5 name: 'brett' year: 2005 day: 'Mon' 6 name: 'mike' year: 2005 day: 'Mon' 7 name: 'jiro' year: 2006 day: 'Mon'
Thanks for this entry, Jake. One recommendation I have is to include a couple of examples in the HELP text in the code.
Edit (R2017a)
From R2017a, you can use sortrows with tables to accomplish a similar task cleanly and efficiently.
Atable = struct2table(A);
Atable_sorted = sortrows(Atable,'name')
Atable_sorted = 7×3 table name year day _________ ____ _______ {'brett'} 2005 {'Mon'} {'doug' } 2001 {'Fri'} {'jiro' } 2006 {'Mon'} {'loren'} 1987 {'Fri'} {'mike' } 2005 {'Mon'} {'seth' } 1998 {'Mon'} {'steve'} 1993 {'Wed'}
Comments
Give this a try and et us know what you think here or leave a comment for Jake.
- 범주:
- Picks
댓글
댓글을 남기려면 링크 를 클릭하여 MathWorks 계정에 로그인하거나 계정을 새로 만드십시오.