Innerjoin when a table contains user-defined objects

I have two tables that I wish to innerjoin() according to the topThick and botThick columns, which are the same in both tables. Why does this fail when Tright contains a column with objects of a user-defined class (here, myclass)?
load testdata
Tleft, Tright
Tleft = 16×4 table
topThick botThick LogImageTop LogImageBot ________ ________ ___________ ___________ 400 400 {[5]} {[5]} 600 400 {[5]} {[5]} 800 400 {[5]} {[5]} 1000 400 {[5]} {[5]} 400 600 {[5]} {[5]} 600 600 {[5]} {[5]} 800 600 {[5]} {[5]} 1000 600 {[5]} {[5]} 400 800 {[5]} {[5]} 600 800 {[5]} {[5]} 800 800 {[5]} {[5]} 1000 800 {[5]} {[5]} 400 1000 {[5]} {[5]} 600 1000 {[5]} {[5]} 800 1000 {[5]} {[5]} 1000 1000 {[5]} {[5]}
Tright = 16×3 table
topThick botThick Var1 ________ ________ ___________ 400 400 1×1 myclass 600 400 1×1 myclass 800 400 1×1 myclass 1000 400 1×1 myclass 400 600 1×1 myclass 600 600 1×1 myclass 800 600 1×1 myclass 1000 600 1×1 myclass 400 800 1×1 myclass 600 800 1×1 myclass 800 800 1×1 myclass 1000 800 1×1 myclass 400 1000 1×1 myclass 600 1000 1×1 myclass 800 1000 1×1 myclass 1000 1000 1×1 myclass
innerjoin(Tleft, Tright)
Error using tabular/innerjoin (line 34)
Not enough input arguments.

 採用された回答

Paul
Paul 約15時間 前
編集済み: Paul 約14時間 前

0 投票

Hi Matt,
I think the class defintion has to change so that the myclass constructor can accept zero arguments.
I believe that will get past the error. You'll have to check if the innerjoin then yields the correct result.
dbtype myclass
1 classdef myclass 2 3 properties 4 p=[]; 5 end 6 methods 7 function obj=myclass(p) 8 obj.p=p; 9 end 10 end 11 12 end
try
myclass()
catch ME
ME.message
end
ans = 'Not enough input arguments.'
lines = readlines("myclass.m");
[(1:numel(lines)).',lines]
ans = 12×2 string array
"1" "classdef myclass" "2" "" "3" " properties" "4" " p=[];" "5" " end" "6" " methods" "7" " function obj=myclass(p)" "8" " obj.p=p;" "9" " end" "10" " end" "11" "" "12" "end"
lines(1) = "classdef newmyclass";
lines(7) = replace(lines(7),"myclass","newmyclass");
lines = [lines(1:7);"if nargin > 0";lines(8);"end";lines(9:end)];
writelines(lines,"newmyclass.m");
dbtype newmyclass.m
1 classdef newmyclass 2 3 properties 4 p=[]; 5 end 6 methods 7 function obj=newmyclass(p) 8 if nargin > 0 9 obj.p=p; 10 end 11 end 12 end 13 14 end
newmyclass()
ans =
newmyclass with properties: p: []
load testdata
Tright.Var1 = repmat(newmyclass(3),height(Tright),1)
Tright = 16×3 table
topThick botThick Var1 ________ ________ ______________ 400 400 1×1 newmyclass 600 400 1×1 newmyclass 800 400 1×1 newmyclass 1000 400 1×1 newmyclass 400 600 1×1 newmyclass 600 600 1×1 newmyclass 800 600 1×1 newmyclass 1000 600 1×1 newmyclass 400 800 1×1 newmyclass 600 800 1×1 newmyclass 800 800 1×1 newmyclass 1000 800 1×1 newmyclass 400 1000 1×1 newmyclass 600 1000 1×1 newmyclass 800 1000 1×1 newmyclass 1000 1000 1×1 newmyclass
innerjoin(Tleft,Tright)
ans = 16×5 table
topThick botThick LogImageTop LogImageBot Var1 ________ ________ ___________ ___________ ______________ 400 400 {[5]} {[5]} 1×1 newmyclass 400 600 {[5]} {[5]} 1×1 newmyclass 400 800 {[5]} {[5]} 1×1 newmyclass 400 1000 {[5]} {[5]} 1×1 newmyclass 600 400 {[5]} {[5]} 1×1 newmyclass 600 600 {[5]} {[5]} 1×1 newmyclass 600 800 {[5]} {[5]} 1×1 newmyclass 600 1000 {[5]} {[5]} 1×1 newmyclass 800 400 {[5]} {[5]} 1×1 newmyclass 800 600 {[5]} {[5]} 1×1 newmyclass 800 800 {[5]} {[5]} 1×1 newmyclass 800 1000 {[5]} {[5]} 1×1 newmyclass 1000 400 {[5]} {[5]} 1×1 newmyclass 1000 600 {[5]} {[5]} 1×1 newmyclass 1000 800 {[5]} {[5]} 1×1 newmyclass 1000 1000 {[5]} {[5]} 1×1 newmyclass

5 件のコメント

Matt J
Matt J 約4時間 前
Surely that's a bug, though. Innerjoin is not supposed to require the creation of new objects.
Matt J
Matt J 約4時間 前
Props, by the way for editing myclass.m in purely programmatic fashion. A lazy person would have just uploaded a new file ;)
Paul
Paul 約3時間 前
編集済み: Paul 約3時間 前
It's not a bug in the sense that down in the bowels of innerjoin it is intentionally creating a vector of new myclass objects, and those objects are instantiated with "default" values. As to why the code operates that way and whether or not it could be implemented differently ... I have no idea. I didn't follow the code after that to see how those default objects get updated to their final values.
At No Input Argument Constructor Requirement the doc discusses two cases (are there others?) where a no-argument constructor is required and I think this situation in innerjoin falls under the second of those.
Matt J
Matt J 約2時間 前
I've asked Tech Support to verify if it was intentional. My feeling is that they inadvertently applied the same programming logic as used in outerjoin to innerjoin as well.
Paul
Paul 約1時間 前
編集済み: Paul 約1時間 前
AFAICT, the underlying function with all of the hot sauce is tabular.joinInnerOuter, which handles both types of joins.
Loosely speaking ...
The first part of that code develops the indices for the elements of the column variables that need to be joined based on if the join is inner or outer.
Once the indices are obtained, then the second step creates a vector of default values for output, then stuffs into that vector for inner, or parts of that vector for outer, the portions of the input variables based on the step 1 indices. I see your point for an inner join that there won't ever be any default values in the output and so could be handled differently (and slightly more efficiently I suppose) in the second step.
At a minimum, perhaps the top level function tabular.innerjoin could do a better job of checking the inputs and verifying that each has nonkey variables with creatable default values and reporting an understandable error message if not.
All of the above based on R2024a.

サインインしてコメントする。

その他の回答 (1 件)

Matt J
Matt J 約10時間 前

0 投票

Attached is a possible workaround.
load testdata
tableInner(Tleft,Tright)
ans = 16×5 table
topThick botThick LogImageTop LogImageBot Var1 ________ ________ ___________ ___________ ___________ 400 400 {[5]} {[5]} 1×1 myclass 400 600 {[5]} {[5]} 1×1 myclass 400 800 {[5]} {[5]} 1×1 myclass 400 1000 {[5]} {[5]} 1×1 myclass 600 400 {[5]} {[5]} 1×1 myclass 600 600 {[5]} {[5]} 1×1 myclass 600 800 {[5]} {[5]} 1×1 myclass 600 1000 {[5]} {[5]} 1×1 myclass 800 400 {[5]} {[5]} 1×1 myclass 800 600 {[5]} {[5]} 1×1 myclass 800 800 {[5]} {[5]} 1×1 myclass 800 1000 {[5]} {[5]} 1×1 myclass 1000 400 {[5]} {[5]} 1×1 myclass 1000 600 {[5]} {[5]} 1×1 myclass 1000 800 {[5]} {[5]} 1×1 myclass 1000 1000 {[5]} {[5]} 1×1 myclass

2 件のコメント

Paul
Paul 約7時間 前
Why not modify the class definition to accept zero arguments in the constructor, if you don't mind me asking?
Matt J
Matt J 約6時間 前
編集済み: Matt J 約6時間 前
It's just not always the desirable behavior in a class design. Sometimes you want an error thrown if someone tries to call a constructor with no arguments.

サインインしてコメントする。

製品

リリース

R2024b

質問済み:

2026 年 6 月 12 日 23:15

編集済み:

約6時間 前

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by