@@ -792,6 +792,8 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
792792 for (unsigned int i = 0 ; i < realObjects.size (); ++i)
793793 parentContext.ObjectsListNeeded (realObjects[i]);
794794
795+ const bool hasOrderBy = !event.GetOrderBy ().empty ();
796+
795797 // Context is "reset" each time the event is repeated (i.e. objects are
796798 // picked again)
797799 gd::EventsCodeGenerationContext context;
@@ -809,6 +811,25 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
809811 for (unsigned int i = 0 ; i < realObjects.size (); ++i)
810812 context.EmptyObjectsListNeeded (realObjects[i]);
811813
814+ // When orderBy is set, we also need a sorting context to generate the
815+ // expression code for evaluating the orderBy expression on each object.
816+ gd::String orderByExpressionCode;
817+ gd::String sortObjectDeclaration;
818+ if (hasOrderBy) {
819+ gd::EventsCodeGenerationContext sortContext;
820+ sortContext.InheritsFrom (parentContext);
821+ sortContext.ForbidReuse ();
822+ for (unsigned int i = 0 ; i < realObjects.size (); ++i)
823+ sortContext.EmptyObjectsListNeeded (realObjects[i]);
824+
825+ orderByExpressionCode =
826+ gd::ExpressionCodeGenerator::GenerateExpressionCode (
827+ codeGenerator, sortContext, " number" ,
828+ gd::Expression (event.GetOrderBy ()));
829+ sortObjectDeclaration =
830+ codeGenerator.GenerateObjectsDeclarationCode (sortContext) + " \n " ;
831+ }
832+
812833 // Prepare conditions/actions codes
813834 gd::String conditionsCode = codeGenerator.GenerateConditionsListCode (
814835 event.GetConditions (), context);
@@ -845,12 +866,29 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
845866 event.GetLoopIndexVariableName (), context);
846867 }
847868
869+ // Declare additional variables for the orderBy sorting phase
870+ gd::String forEachSortedList;
871+ gd::String forEachSortKeysList;
872+ gd::String forEachLimitVar;
873+ if (hasOrderBy) {
874+ forEachSortedList =
875+ codeGenerator.GetCodeNamespaceAccessor () + " forEachSorted" +
876+ gd::String::From (context.GetContextDepth ());
877+ codeGenerator.AddGlobalDeclaration (forEachSortedList + " = [];\n " );
878+ forEachSortKeysList =
879+ codeGenerator.GetCodeNamespaceAccessor () + " forEachSortKeys" +
880+ gd::String::From (context.GetContextDepth ());
881+ codeGenerator.AddGlobalDeclaration (forEachSortKeysList + " = [];\n " );
882+ forEachLimitVar =
883+ codeGenerator.GetCodeNamespaceAccessor () + " forEachLimit" +
884+ gd::String::From (context.GetContextDepth ());
885+ codeGenerator.AddGlobalDeclaration (forEachLimitVar + " = 0;\n " );
886+ }
887+
848888 outputCode += localVariablesInitializationCode;
849889
850- if (realObjects.size () !=
851- 1 ) // (We write a slightly more simple ( and optimized ) output code
852- // when only one object list is used.)
853- {
890+ // --- Build the combined objects list ---
891+ if (realObjects.size () != 1 ) {
854892 outputCode += forEachTotalCountVar + " = 0;\n " ;
855893 outputCode += forEachObjectsList + " .length = 0;\n " ;
856894 for (unsigned int i = 0 ; i < realObjects.size (); ++i) {
@@ -873,66 +911,196 @@ CommonInstructionsExtension::CommonInstructionsExtension() {
873911 }
874912 }
875913
876- // Write final code :
914+ if (hasOrderBy) {
915+ // --- OrderBy sorting phase ---
916+ // Build a combined list (always use forEachObjectsList for sorting)
917+ if (realObjects.size () == 1 ) {
918+ // For single object: copy to the combined list for uniform handling
919+ outputCode += forEachObjectsList + " .length = 0;\n " ;
920+ outputCode +=
921+ forEachObjectsList + " .push.apply(" + forEachObjectsList +
922+ " ," +
923+ codeGenerator.GetObjectListName (realObjects[0 ], parentContext) +
924+ " );\n " ;
925+ outputCode += forEachTotalCountVar + " = " + forEachObjectsList +
926+ " .length;\n " ;
927+ }
877928
878- // For loop declaration
879- if (realObjects.size () ==
880- 1 ) // We write a slightly more simple ( and optimized ) output code
881- // when only one object list is used.
929+ // Evaluate the orderBy expression for each object and store in sort keys
930+ outputCode += forEachSortKeysList + " .length = 0;\n " ;
882931 outputCode +=
883932 " for (" + forEachIndexVar + " = 0;" + forEachIndexVar + " < " +
884- codeGenerator.GetObjectListName (realObjects[0 ], parentContext) +
885- " .length;++" + forEachIndexVar + " ) {\n " ;
886- else
887- outputCode += " for (" + forEachIndexVar + " = 0;" + forEachIndexVar +
888- " < " + forEachTotalCountVar + " ;++" + forEachIndexVar +
889- " ) {\n " ;
890-
891- // Empty object lists declaration
892- outputCode += objectDeclaration;
933+ forEachTotalCountVar + " ;++" + forEachIndexVar + " ) {\n " ;
934+
935+ // Pick one object for evaluation
936+ outputCode += sortObjectDeclaration;
937+ for (unsigned int i = 0 ; i < realObjects.size (); ++i) {
938+ if (realObjects.size () == 1 ) {
939+ outputCode +=
940+ codeGenerator.GetObjectListName (realObjects[0 ], context) +
941+ " .push(" + forEachObjectsList + " [" + forEachIndexVar +
942+ " ]);\n " ;
943+ } else {
944+ gd::String count;
945+ for (unsigned int j = 0 ; j <= i; ++j) {
946+ gd::String forEachCountVar =
947+ codeGenerator.GetCodeNamespaceAccessor () + " forEachCount" +
948+ gd::String::From (j) + " _" +
949+ gd::String::From (context.GetContextDepth ());
950+ if (j != 0 )
951+ count += " +" ;
952+ count += forEachCountVar;
953+ }
954+ if (i != 0 )
955+ outputCode += " else " ;
956+ outputCode +=
957+ " if (" + forEachIndexVar + " < " + count + " ) {\n " ;
958+ outputCode +=
959+ " " +
960+ codeGenerator.GetObjectListName (realObjects[i], context) +
961+ " .push(" + forEachObjectsList + " [" + forEachIndexVar +
962+ " ]);\n " ;
963+ outputCode += " }\n " ;
964+ }
965+ }
966+
967+ outputCode += forEachSortKeysList + " .push(" +
968+ orderByExpressionCode + " );\n " ;
969+ outputCode += " }\n " ; // End of sort key evaluation loop
970+
971+ // Build sorted indices and sort them
972+ outputCode += forEachSortedList + " .length = 0;\n " ;
973+ outputCode += " for (" + forEachIndexVar + " = 0;" +
974+ forEachIndexVar + " < " + forEachTotalCountVar + " ;++" +
975+ forEachIndexVar + " ) " + forEachSortedList + " .push(" +
976+ forEachIndexVar + " );\n " ;
893977
894- // Pick one object
895- if (realObjects.size () == 1 ) {
896- // We write a slightly more simple ( and optimized ) output code
897- // when only one object list is used.
898- gd::String temporary = codeGenerator.GetCodeNamespaceAccessor () +
899- " forEachTemporary" +
900- gd::String::From (context.GetContextDepth ());
901- codeGenerator.AddGlobalDeclaration (temporary + " = null;\n " );
978+ gd::String isDesc =
979+ event.GetOrder () == " desc" ? " true" : " false" ;
902980 outputCode +=
903- temporary + " = " +
904- codeGenerator.GetObjectListName (realObjects[0 ], parentContext) +
905- " [" + forEachIndexVar + " ];\n " ;
981+ forEachSortedList + " .sort(function(a, b) { return " + isDesc +
982+ " ? " + forEachSortKeysList + " [b] - " + forEachSortKeysList +
983+ " [a] : " + forEachSortKeysList + " [a] - " +
984+ forEachSortKeysList + " [b]; });\n " ;
985+
986+ // Apply limit
987+ const bool hasLimit = !event.GetLimit ().empty ();
988+ if (hasLimit) {
989+ gd::String limitCode =
990+ gd::ExpressionCodeGenerator::GenerateExpressionCode (
991+ codeGenerator, parentContext, " number" ,
992+ gd::Expression (event.GetLimit ()));
993+ outputCode += forEachLimitVar + " = " + limitCode + " ;\n " ;
994+ outputCode += " if (" + forEachLimitVar + " > 0 && " +
995+ forEachSortedList + " .length > " + forEachLimitVar +
996+ " ) " + forEachSortedList + " .length = " +
997+ forEachLimitVar + " ;\n " ;
998+ }
906999
1000+ // Iterate through sorted indices
9071001 outputCode +=
908- codeGenerator.GetObjectListName (realObjects[0 ], context) +
909- " .push(" + temporary + " );\n " ;
910- } else {
911- // Generate the code to pick only one object in the lists
1002+ " for (" + forEachIndexVar + " = 0;" + forEachIndexVar + " < " +
1003+ forEachSortedList + " .length;++" + forEachIndexVar + " ) {\n " ;
1004+
1005+ // Empty object lists and pick the right object
1006+ outputCode += objectDeclaration;
9121007 for (unsigned int i = 0 ; i < realObjects.size (); ++i) {
913- gd::String count;
914- for (unsigned int j = 0 ; j <= i; ++j) {
915- gd::String forEachCountVar =
916- codeGenerator.GetCodeNamespaceAccessor () + " forEachCount" +
917- gd::String::From (j) + " _" +
1008+ if (realObjects.size () == 1 ) {
1009+ gd::String temporary =
1010+ codeGenerator.GetCodeNamespaceAccessor () +
1011+ " forEachTemporary" +
9181012 gd::String::From (context.GetContextDepth ());
919-
920- if (j != 0 )
921- count += " +" ;
922- count += forEachCountVar;
1013+ codeGenerator.AddGlobalDeclaration (temporary + " = null;\n " );
1014+ outputCode +=
1015+ temporary + " = " + forEachObjectsList + " [" +
1016+ forEachSortedList + " [" + forEachIndexVar + " ]];\n " ;
1017+ outputCode +=
1018+ codeGenerator.GetObjectListName (realObjects[0 ], context) +
1019+ " .push(" + temporary + " );\n " ;
1020+ } else {
1021+ gd::String count;
1022+ for (unsigned int j = 0 ; j <= i; ++j) {
1023+ gd::String forEachCountVar =
1024+ codeGenerator.GetCodeNamespaceAccessor () + " forEachCount" +
1025+ gd::String::From (j) + " _" +
1026+ gd::String::From (context.GetContextDepth ());
1027+ if (j != 0 )
1028+ count += " +" ;
1029+ count += forEachCountVar;
1030+ }
1031+ if (i != 0 )
1032+ outputCode += " else " ;
1033+ outputCode += " if (" + forEachSortedList + " [" + forEachIndexVar +
1034+ " ] < " + count + " ) {\n " ;
1035+ outputCode +=
1036+ " " +
1037+ codeGenerator.GetObjectListName (realObjects[i], context) +
1038+ " .push(" + forEachObjectsList + " [" + forEachSortedList +
1039+ " [" + forEachIndexVar + " ]]);\n " ;
1040+ outputCode += " }\n " ;
9231041 }
1042+ }
1043+ } else {
1044+ // --- Standard (no orderBy) path ---
1045+
1046+ // For loop declaration
1047+ if (realObjects.size () == 1 )
1048+ outputCode +=
1049+ " for (" + forEachIndexVar + " = 0;" + forEachIndexVar + " < " +
1050+ codeGenerator.GetObjectListName (realObjects[0 ], parentContext) +
1051+ " .length;++" + forEachIndexVar + " ) {\n " ;
1052+ else
1053+ outputCode +=
1054+ " for (" + forEachIndexVar + " = 0;" + forEachIndexVar + " < " +
1055+ forEachTotalCountVar + " ;++" + forEachIndexVar + " ) {\n " ;
1056+
1057+ // Empty object lists declaration
1058+ outputCode += objectDeclaration;
1059+
1060+ // Pick one object
1061+ if (realObjects.size () == 1 ) {
1062+ gd::String temporary =
1063+ codeGenerator.GetCodeNamespaceAccessor () +
1064+ " forEachTemporary" +
1065+ gd::String::From (context.GetContextDepth ());
1066+ codeGenerator.AddGlobalDeclaration (temporary + " = null;\n " );
1067+ outputCode +=
1068+ temporary + " = " +
1069+ codeGenerator.GetObjectListName (realObjects[0 ], parentContext) +
1070+ " [" + forEachIndexVar + " ];\n " ;
9241071
925- if (i != 0 )
926- outputCode += " else " ;
927- outputCode += " if (" + forEachIndexVar + " < " + count + " ) {\n " ;
9281072 outputCode +=
929- " " +
930- codeGenerator.GetObjectListName (realObjects[i], context) +
931- " .push(" + forEachObjectsList + " [" + forEachIndexVar + " ]);\n " ;
932- outputCode += " }\n " ;
1073+ codeGenerator.GetObjectListName (realObjects[0 ], context) +
1074+ " .push(" + temporary + " );\n " ;
1075+ } else {
1076+ for (unsigned int i = 0 ; i < realObjects.size (); ++i) {
1077+ gd::String count;
1078+ for (unsigned int j = 0 ; j <= i; ++j) {
1079+ gd::String forEachCountVar =
1080+ codeGenerator.GetCodeNamespaceAccessor () + " forEachCount" +
1081+ gd::String::From (j) + " _" +
1082+ gd::String::From (context.GetContextDepth ());
1083+
1084+ if (j != 0 )
1085+ count += " +" ;
1086+ count += forEachCountVar;
1087+ }
1088+
1089+ if (i != 0 )
1090+ outputCode += " else " ;
1091+ outputCode +=
1092+ " if (" + forEachIndexVar + " < " + count + " ) {\n " ;
1093+ outputCode +=
1094+ " " +
1095+ codeGenerator.GetObjectListName (realObjects[i], context) +
1096+ " .push(" + forEachObjectsList + " [" + forEachIndexVar +
1097+ " ]);\n " ;
1098+ outputCode += " }\n " ;
1099+ }
9331100 }
9341101 }
9351102
1103+ // --- Common iteration body (both ordered and unordered) ---
9361104 if (hasIndexVariable) {
9371105 outputCode +=
9381106 indexVariableAccessor + " .setNumber(" + forEachIndexVar + " );\n " ;
0 commit comments