Code covered by the BSD License  

Highlights from
RenameField

image thumbnail

RenameField

by

 

20 Aug 2010 (Updated )

Rename a fields of a struct - fast C-Mex

uTest_RenameField(doSpeed)
function uTest_RenameField(doSpeed)
% Automatic test: RenameField
% This is a routine for automatic testing. It is not needed for processing and
% can be deleted or moved to a folder, where it does not bother.
%
% uTest_RenameField(doSpeed)
% INPUT:
%   doSpeed: Optional logical flag to trigger time consuming speed tests.
%            Default: TRUE. If no speed test is defined, this is ignored.
% OUTPUT:
%   On failure the test stops with an error.
%
% Tested: Matlab 6.5, 7.7, 7.8, WinXP, 32 bit
% Author: Jan Simon, Heidelberg, (C) 2009-2011 matlab.THISYEAR(a)nMINUSsimon.de

% $JRev: R-p V:015 Sum:UsDS07hXEq26 Date:12-Feb-2011 00:46:28 $
% $License: BSD (see Docs\BSD_License.txt) $
% $File: Tools\UnitTests_\uTest_RenameField.m $
% History:
% 015: 11-Feb-2011 11:54, Check cell string input.

% Initialize: ==================================================================
ErrID = ['JSim:', mfilename];

if nargin == 0
   doSpeed = true;
end

whichFun  = which('RenameField');
C10 = char(10);

disp(['==== Test RenameField:  ', datestr(now, 0), C10, ...
      'Versions: ', whichFun, C10]);

disp('== Known answer tests:');
disp('  String method');
S = [];
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'double'))
   error([ErrID, 'KATFailed'], 'Bad reply for S=[]');
end

S = struct([]);
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isempty(fieldnames(R)))
   error([ErrID, 'KATFailed'], 'Bad reply for S=struct([])');
end

S = struct('a', {});
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a');
end

S = struct('a', 1);
R = RenameField(S, 'old', 'new');
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'a'}))
   error([ErrID, 'KATFailed'], 'Bad reply for S.a');
end

S = struct('a', {1, 2});
R = RenameField(S, 'old', 'new');
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'a'}) && ....
      isequal(size(R), [1, 2]))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S(1:2).a');
end

S = struct('a', {}, 'b', {});
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'; 'b'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a, S.b');
end

S = struct('a', 1, 'b', 2);
R = RenameField(S, 'old', 'new');
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.a, S.b');
end

S = struct('a', {1, 2}, 'b', {3, 4});
R = RenameField(S, 'old', 'new');
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).b');
end

% ----------------------
S = struct('old', {});
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.old');
end

S = struct('old', 1);
R = RenameField(S, 'old', 'new');
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for S.old');
end

S = struct('old', {1, 2});
R = RenameField(S, 'old', 'new');
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'new'}) && ....
      isequal(size(R), [1, 2]))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S(1:2).old');
end

S = struct('old', {}, 'b', {});
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'new'; 'b'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.old, S.b');
end

S = struct('old', 1, 'b', 2);
R = RenameField(S, 'old', 'new');
if ~isequal(R, struct('new', 1, 'b', 2))
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.old, S.b');
end

S = struct('old', {1, 2}, 'b', {3, 4});
R = RenameField(S, 'old', 'new');
if ~isequal(R, struct('new', {1, 2}, 'b', {3, 4}))
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).old, S(1:2).b');
end

% ---------------------------
S = struct('a', {}, 'old', {});
R = RenameField(S, 'old', 'new');
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'; 'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a, S.old');
end

S = struct('a', 1, 'old', 2);
R = RenameField(S, 'old', 'new');
if ~isequal(R, struct('a', 1, 'new', 2))
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.a, S.old');
end

S = struct('a', {1, 2}, 'old', {3, 4});
R = RenameField(S, 'old', 'new');
if ~isequal(R, struct('a', {1, 2}, 'new', {3, 4}))
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).old');
end

S = struct('a', {1, 2}, 'new', {3, 4});
R = RenameField(S, 'new', 'new');
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).new');
end

disp('  ok');

% ---------------------------
disp('  Cell string method');
S = [];
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'double'))
   error([ErrID, 'KATFailed'], 'Bad reply for S=[]');
end

R = RenameField(S, {}, {});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S=[], New={}');
end

S = struct([]);
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isempty(fieldnames(R)))
   error([ErrID, 'KATFailed'], 'Bad reply for S=struct([])');
end

S = struct('a', {});
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a');
end

R = RenameField(S, {}, {});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a and New={}');
end

S = struct('a', 1);
R = RenameField(S, {'old'}, {'new'});
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'a'}))
   error([ErrID, 'KATFailed'], 'Bad reply for S.a');
end

R = RenameField(S, {}, {});
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'a'}))
   error([ErrID, 'KATFailed'], 'Bad reply for S.a and New={}');
end

S = struct('a', {1, 2});
R = RenameField(S, {'old'}, {'new'});
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'a'}) && ....
      isequal(size(R), [1, 2]))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S(1:2).a');
end

S = struct('a', {}, 'b', {});
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'; 'b'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a, S.b');
end

S = struct('a', 1, 'b', 2);
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.a, S.b');
end

S = struct('a', {1, 2}, 'b', {3, 4});
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).b');
end

R = RenameField(S, {}, {});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).b and New={}');
end

% ----------------------
S = struct('old', {});
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.old');
end

S = struct('old', 1);
R = RenameField(S, {'old'}, {'new'});
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for S.old');
end

S = struct('old', {1, 2});
R = RenameField(S, {'old'}, {'new'});
if ~(isa(R, 'struct') && isequal(fieldnames(R), {'new'}) && ....
      isequal(size(R), [1, 2]))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S(1:2).old');
end

S = struct('old', {}, 'b', {});
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'new'; 'b'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.old, S.b');
end

S = struct('old', 1, 'b', 2);
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, struct('new', 1, 'b', 2))
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.old, S.b');
end

S = struct('old', {1, 2}, 'b', {3, 4});
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, struct('new', {1, 2}, 'b', {3, 4}))
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).old, S(1:2).b');
end

% ---------------------------
S = struct('a', {}, 'old', {});
R = RenameField(S, {'old'}, {'new'});
if ~(isempty(R) && isa(R, 'struct') && isequal(fieldnames(R), {'a'; 'new'}))
   error([ErrID, 'KATFailed'], 'Bad reply for empty S.a, S.old');
end

S = struct('a', 1, 'old', 2);
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, struct('a', 1, 'new', 2))
   error([ErrID, 'KATFailed'], 'Bad reply for scalar S.a, S.old');
end

S = struct('a', {1, 2}, 'old', {3, 4});
R = RenameField(S, {'old'}, {'new'});
if ~isequal(R, struct('a', {1, 2}, 'new', {3, 4}))
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).old');
end

S = struct('a', {1, 2}, 'new', {3, 4});
R = RenameField(S, {'new'}, {'new'});
if ~isequal(R, S)
   error([ErrID, 'KATFailed'], 'Bad reply for S(1:2).a, S(1:2).new');
end

S = struct('a', {1, 2}, 'b', {3, 4});
R = RenameField(S, {'a', 'b'}, {'a2', 'b2'});
Want = struct('a2', {1, 2}, 'b2', {3, 4});
if ~isequal(R, Want)
   error([ErrID, 'KATFailed'], ...
      'Bad reply for S(1:2).a, S(1:2).old, {a,b}->{a2,b2}');
end

R = RenameField(S, {'b', 'a'}, {'b2', 'a2'});
if ~isequal(R, Want)
   error([ErrID, 'KATFailed'], ...
      'Bad reply for S(1:2).a, S(1:2).old, {b,a}->{b2,a2}');
end

R = RenameField(S, {'b', 'a', 'notExisting'}, {'b2', 'a2', 'notExisting2'});
if ~isequal(R, Want)
   error([ErrID, 'KATFailed'], ...
      'Bad reply for S(1:2).a, S(1:2).old, {b,a,miss}->{b2,a2,miss2}');
end

R = RenameField(S, {'b', 'notExisting', 'a'}, {'b2', 'notExisting2', 'a2'});
if ~isequal(R, Want)
   error([ErrID, 'KATFailed'], ...
      'Bad reply for S(1:2).a, S(1:2).old, {b,miss,a}->{b2,miss2,a2}');
end

R = RenameField(S, {'notExisting', 'b', 'a'}, {'notExisting2', 'b2', 'a2'});
if ~isequal(R, Want)
   error([ErrID, 'KATFailed'], ...
      'Bad reply for S(1:2).a, S(1:2).old, {miss,b,a}->{miss2,b2,a2}');
end

disp('  ok');

% ---------------------------
disp('== Provoke errors:');
% Length of the name is checked even with disabled validity checks, because
% Matlab's memory management gets corrupted for a too long name!
S = struct('a', 1, 'b', 2, 'c', 3);
tooLazy = false;
try
   R       = RenameField(S, 'a', repmat('a', 1, 64));
   tooLazy = true;
catch
   % Nothing to do
end
if tooLazy
   error([ErrID, 'InvalidNameAccepted'], '64 characters accepted!');
end

S = struct('a', 1, 'b', 2, 'c', 3);
try
   R = RenameField(S, 'a', '_');
   disp('  dangerous: No check for valid field name!');
   CheckEnabled = false;
catch
   disp('  ok: Check for valid field name is enabled!');
   CheckEnabled = true;
end

if CheckEnabled
   try
      R       = RenameField(S, 'a', '0');
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'Duplicate name accepted!');
   end
   
   try
      R       = RenameField(S, 'a', '');
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'Empty name accepted!');
   end
   
   try
      R       = RenameField(S, {'a'}, {'_a'});
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'Leading underscore accepted!');
   end

   try
      R       = RenameField(S, 'a', '0a');
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'Leading number accepted!');
   end
   
   try
      R       = RenameField(S, 'a', [98, 99]);
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'DOUBLE accepted!');
   end
   
   try
      R       = RenameField(S, 'a', 'b_', 'c_');
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], '4 inputs accepted!');
   end
   
   try
      R       = RenameField(S, 'a', {'b'});
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'String+Cell input accepted!');
   end
   
   try
      R       = RenameField(S, {'a'}, 'b');
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], 'Cell+String input accepted!');
   end
   
   try
      R       = RenameField(S, {'a'}, {'b', 'c'});
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], ...
         'Cell input with different sizes accepted!');
   end

   try
      R       = RenameField(S, cell(1, 2), {'b', 'c'});
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], ...
         'Unitialized cell elements in [Old] accepted!');
   end

   try
      R       = RenameField(S, {'a', 'b'}, cell(1, 2));
      tooLazy = false;
   catch
      % Nothing to do
   end
   if tooLazy
      error([ErrID, 'InvalidNameAccepted'], ...
         'Unitialized cell elements in [New] accepted!');
   end
end

disp('  ok');

% Speed: -----------------------------------------------------------------------
if doSpeed
   TestTime = 1.0;  % sec
   fprintf('== Speed test (test time: %g sec):\n', TestTime);
else
   TestTime = 0.1;
   fprintf('\n== Speed test (test time: %g sec - may be inaccurate):\n', ...
      TestTime);
end

% Determine number of loops:
C = cell(1000, 1);
D = cell(1000, 1);
E = cell(1000, 1);
for iC = 1:1000
   C{iC} = sprintf('field%d', iC);
   D{iC} = rand(8, 8);
   E{iC} = sprintf('new%d', iC);
end

S1000 = cell2struct(D, C);
iTime = cputime;
iLoop = 0;
while cputime - iTime < TestTime
   v = RenameField(S1000, 'field500', 'newname');  %#ok<*NASGU>
   clear('v');   % Suppress JIT acceleration for realistic times
   iLoop = iLoop + 1;
end
nDigit = max(1, floor(log10(max(1, iLoop))) - 1);
nLoop  = max(2, round(iLoop / 10 ^ nDigit) * 10 ^ nDigit);

fprintf('STRING - rename 1 field of a struct:  (%d loops)\n', nLoop);
for nField = [1, 10, 100, 1000]
   fprintf('  Struct has %d fields:\n', ...
      nField);
   pause(0.02);  % Flush Java event queue to show the message
   
   S = cell2struct(D(1:nField), C(1:nField));
   tic;
   for iLoop = 1:nLoop
      T = RenameField_local(S, C{rem(iLoop, nField) + 1}, 'newname');
      clear('T');
   end
   M_time = toc + eps;
   
   tic;
   for iLoop = 1:nLoop
      T = RenameField(S, C{rem(iLoop, nField) + 1}, 'newname');
      clear('T');
   end
   Mex_time = toc;
   
   fprintf('    M:  %7.3f sec   Mex:%7.3f sec  ==> %4.1f%% of M-version\n', ...
      M_time, Mex_time, 100 * Mex_time / M_time);
end

nLoop = nLoop * 10;
fprintf('\nCELL STRING - rename all fields of a struct:  (%d loops)\n', nLoop);
for nField = [1, 10, 100, 1000]
   fprintf('  Struct has %d fields:\n', nField);
   pause(0.02);  % Flush Java event queue to show the message
   
   C_ = C(1:nField);
   E_ = E(1:nField);
   
   S = cell2struct(D(1:nField), C_);
   tic;
   for iLoop = 1:nLoop / nField
      T = RenameFieldC_local(S, C_, E_);
      clear('T');
   end
   M_time = toc + eps;
   
   tic;
   for iLoop = 1:nLoop / nField
      T = RenameField(S, C_, E_);
      clear('T');
   end
   Mex_time = toc;
   fprintf('    M:  %7.3f sec   Mex:%7.3f sec  ==> %4.1f%% of M-version\n', ...
      M_time, Mex_time, 100 * Mex_time / M_time);
end

fprintf('\n== RenameField passed the tests.\n');

return;

% ==============================================================================
function S = RenameField_local(S, Old, New)
% A simple and fairly efficient M-version:

Data  = struct2cell(S);
Field = fieldnames(S);
Field{strcmp(Field, Old)} = New;
S     = cell2struct(Data, Field);

return;

% ==============================================================================
function S = RenameFieldC_local(S, Old, New)
% A simple and fairly efficient M-version:

Data  = struct2cell(S);
Field = fieldnames(S);
for i = 1:numel(Old)
   Field{strcmp(Field, Old{i})} = New{i};
end
S     = cell2struct(Data, Field);

return;

Contact us