Skip to content

Commit 764dc6e

Browse files
committed
[bug] smart array that allows expansion, make jd.v(i)=val work
1 parent 56013db commit 764dc6e

File tree

2 files changed

+64
-17
lines changed

2 files changed

+64
-17
lines changed

jdict.m

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@
575575
end
576576
opcell{1} = obj.data;
577577

578+
% forward value extraction loop
578579
for i = 1:oplen
579580
idx = idxkey(i);
580581
if (strcmp(idx.type, '.'))
@@ -597,8 +598,26 @@
597598
continue
598599
end
599600
if (ischar(idx.subs) && strcmp(idx.subs, 'v') && i < oplen && strcmp(idxkey(i + 1).type, '()'))
601+
% expand struct or cell when using .v(index) more
602+
% than the length
603+
nextsubs = idxkey(i + 1).subs;
604+
if iscell(nextsubs)
605+
nextsubs = nextsubs{1};
606+
end
607+
if isnumeric(nextsubs) && isscalar(nextsubs)
608+
if isstruct(opcell{i}) && nextsubs > numel(opcell{i})
609+
fnames = fieldnames(opcell{i});
610+
if (~isempty(fnames))
611+
for fi = 1:length(fnames)
612+
opcell{i}(nextsubs).(fnames{fi}) = [];
613+
end
614+
end
615+
elseif iscell(opcell{i}) && nextsubs > numel(opcell{i})
616+
opcell{i}{nextsubs} = [];
617+
end
618+
end
600619
opcell{i + 1} = opcell{i};
601-
if (i < oplen && iscell(opcell{i}))
620+
if iscell(opcell{i})
602621
idxkey(i + 1).type = '{}';
603622
end
604623
continue
@@ -635,25 +654,44 @@
635654
end
636655
end
637656

638-
if (obj.flags__.isoctave_) && (ismap_(obj.flags__, opcell{i}))
639-
opcell{i}(idx.subs) = otherobj;
640-
opcell{end - 1} = opcell{i};
657+
if (oplen >= 2 && ischar(idxkey(oplen - 1).subs) && strcmp(idxkey(oplen - 1).subs, 'v') && strcmp(idxkey(oplen).type, '()'))
658+
% Handle .v(index) = value at any depth
659+
nextsubs = idxkey(oplen).subs;
660+
if iscell(nextsubs)
661+
nextsubs = nextsubs{1};
662+
end
663+
if iscell(opcell{oplen})
664+
opcell{oplen}{nextsubs} = otherobj;
665+
elseif isstruct(opcell{oplen}) && isempty(fieldnames(opcell{oplen}))
666+
% Empty struct with no fields - just replace
667+
opcell{oplen} = otherobj;
668+
else
669+
opcell{oplen}(nextsubs) = otherobj;
670+
end
671+
opcell{oplen + 1} = opcell{oplen};
672+
elseif (obj.flags__.isoctave_) && (ismap_(obj.flags__, opcell{oplen}))
673+
opcell{oplen}(idx.subs) = otherobj;
674+
opcell{oplen + 1} = opcell{oplen};
641675
else
642676
if (ischar(idx.subs) && ~isempty(idx.subs) && idx.subs(1) == char(36))
643-
opcell{end - 1} = obj.call_('jsonpath', opcell{i}, idx.subs, otherobj);
677+
opcell{oplen + 1} = obj.call_('jsonpath', opcell{oplen}, idx.subs, otherobj);
644678
else
645-
if (ismap_(obj.flags__, opcell{i}))
679+
if (ismap_(obj.flags__, opcell{oplen}))
646680
idx = struct('type', '()', 'subs', idx.subs);
647681
end
648682
try
649-
opcell{end - 1} = subsasgn(opcell{i}, idx, otherobj);
683+
opcell{oplen + 1} = subsasgn(opcell{oplen}, idx, otherobj);
650684
catch
651-
opcell{i}.(idx.subs) = otherobj;
652-
opcell{end - 1} = opcell{i};
685+
opcell{oplen}.(idx.subs) = otherobj;
686+
opcell{oplen + 1} = opcell{oplen};
653687
end
654688
end
655689
end
656690

691+
% Propagate result for backward loop
692+
opcell{oplen} = opcell{oplen + 1};
693+
694+
% backward assignment along the reversed path
657695
for i = oplen - 1:-1:1
658696
idx = idxkey(i);
659697
if (ischar(idx.subs) && strcmp(idx.type, '.') && ismap_(obj.flags__, opcell{i}))
@@ -677,7 +715,16 @@
677715
end
678716

679717
if (i > 1 && ischar(idxkey(i - 1).subs) && strcmp(idxkey(i - 1).subs, 'v'))
680-
if (iscell(opcell{i}) && ~isempty(idx.subs))
718+
if (~isempty(idx.subs) && (iscell(opcell{i}) || (isstruct(opcell{i}) && ~isempty(fieldnames(opcell{i})))))
719+
% Add missing fields to opcell{i} if opcell{i+1} has more fields
720+
if isstruct(opcell{i}) && isstruct(opcell{i + 1})
721+
newfields = fieldnames(opcell{i + 1});
722+
for fi = 1:length(newfields)
723+
if ~isfield(opcell{i}, newfields{fi})
724+
[opcell{i}.(newfields{fi})] = deal([]);
725+
end
726+
end
727+
end
681728
opcell{i} = subsasgn(opcell{i}, idx, opcell{i + 1});
682729
else
683730
opcell{i} = opcell{i + 1};

test/run_jsonlab_test.m

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -499,22 +499,22 @@ function run_jsonlab_test(tests)
499499

500500
% Test basic struct assignment to empty struct array
501501
person = jdict(struct('name', {}, 'age', {}, 'gender', {}));
502-
person.(1) = struct('name', 'Jar Jar', 'age', 100, 'gender', 'M');
502+
person.v(1) = struct('name', 'Jar Jar', 'age', 100, 'gender', 'M');
503503
test_jsonlab('person.(1) = struct(...)', @savejson, person, '{"name":"Jar Jar","age":100,"gender":"M"}', 'compact', 1);
504504

505505
% Test append second struct
506-
person.(2) = struct('name', 'Jane', 'age', 25, 'gender', 'F');
506+
person.v(2) = struct('name', 'Jane', 'age', 25, 'gender', 'F');
507507
test_jsonlab('person.(2) = struct(...)', @savejson, person, '[{"name":"Jar Jar","age":100,"gender":"M"},{"name":"Jane","age":25,"gender":"F"}]', 'compact', 1);
508508

509509
% Test field-by-field assignment to new index
510-
person.(3).name = 'Bob';
511-
person.(3).age = 40;
512-
person.(3).gender = 'M';
510+
person.v(3).name = 'Bob';
511+
person.v(3).age = 40;
512+
person.v(3).gender = 'M';
513513
test_jsonlab('person.(3).name/age/gender', @savejson, person.v(3), '{"name":"Bob","age":40,"gender":"M"}', 'compact', 1);
514514

515515
% Test modify existing element
516-
person.(1).name = 'Jar Jar Modified';
517-
person.(1).age = 101;
516+
person.v(1).name = 'Jar Jar Modified';
517+
person.v(1).age = 101;
518518
test_jsonlab('person.(1).name = ...', @savejson, person.v(1), '{"name":"Jar Jar Modified","age":101,"gender":"M"}', 'compact', 1);
519519

520520
clear testdata jd person;

0 commit comments

Comments
 (0)