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.

 Akzeptierte Antwort

Paul
Paul vor etwa 15 Stunden
Bearbeitet: Paul vor etwa 14 Stunden
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 Kommentare

Matt J
Matt J vor etwa 4 Stunden
Surely that's a bug, though. Innerjoin is not supposed to require the creation of new objects.
Matt J
Matt J vor etwa 4 Stunden
Props, by the way for editing myclass.m in purely programmatic fashion. A lazy person would have just uploaded a new file ;)
Paul
Paul vor etwa 3 Stunden
Bearbeitet: Paul vor etwa 3 Stunden
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 vor etwa 2 Stunden
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 vor etwa eine Stunde
Bearbeitet: Paul vor etwa eine Stunde
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.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (1)

Matt J
Matt J vor etwa 19 Stunden
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 Kommentare

Paul
Paul vor etwa 16 Stunden
Why not modify the class definition to accept zero arguments in the constructor, if you don't mind me asking?
Matt J
Matt J vor etwa 15 Stunden
Bearbeitet: Matt J vor etwa 15 Stunden
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.

Melden Sie sich an, um zu kommentieren.

Produkte

Version

R2024b

Gefragt:

am 12 Jun. 2026 um 23:15

Bearbeitet:

vor etwa 18 Stunden

Community Treasure Hunt

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

Start Hunting!

Translated by