@@ -776,19 +776,30 @@ const func_builtins & value_array_t::get_builtins() const {
776776 if (!is_val<value_array>(args.get_pos (0 ))) {
777777 throw raised_exception (" join() first argument must be an array" );
778778 }
779- value val_delim = args.get_kwarg_or_pos (" d" , 1 );
780- value val_attribute = args.get_kwarg_or_pos (" attribute" , 2 );
781- if (!val_attribute->is_undefined ()) {
782- throw not_implemented_exception (" array attribute join not implemented" );
783- }
779+ value val_delim = args.get_kwarg_or_pos (" d" , 1 );
780+ value attribute = args.get_kwarg_or_pos (" attribute" , 2 );
784781 const auto & arr = args.get_pos (0 )->as_array ();
785- std::string delim = is_val<value_string>(val_delim) ? val_delim->as_string ().str () : " " ;
782+ const bool attr_is_int = is_val<value_int>(attribute);
783+ if (!attribute->is_undefined () && !is_val<value_string>(attribute) && !attr_is_int) {
784+ throw raised_exception (" join() attribute must be string or integer" );
785+ }
786+ const int64_t attr_int = attr_is_int ? attribute->as_int () : 0 ;
787+ const std::string delim = val_delim->is_undefined () ? " " : val_delim->as_string ().str ();
788+ const std::string attr_name = attribute->is_undefined () ? " " : attribute->as_string ().str ();
786789 std::string result;
787790 for (size_t i = 0 ; i < arr.size (); ++i) {
788- if (!is_val<value_string>(arr[i]) && !is_val<value_int>(arr[i]) && !is_val<value_float>(arr[i])) {
791+ value val_arr = arr[i];
792+ if (!attribute->is_undefined ()) {
793+ if (attr_is_int && is_val<value_array>(val_arr)) {
794+ val_arr = val_arr->at (attr_int);
795+ } else if (!attr_is_int && !attr_name.empty () && is_val<value_object>(val_arr)) {
796+ val_arr = val_arr->at (attr_name);
797+ }
798+ }
799+ if (!is_val<value_string>(val_arr) && !is_val<value_int>(val_arr) && !is_val<value_float>(val_arr)) {
789800 throw raised_exception (" join() can only join arrays of strings or numerics" );
790801 }
791- result += arr[i] ->as_string ().str ();
802+ result += val_arr ->as_string ().str ();
792803 if (i < arr.size () - 1 ) {
793804 result += delim;
794805 }
@@ -803,26 +814,30 @@ const func_builtins & value_array_t::get_builtins() const {
803814 }},
804815 {" tojson" , tojson},
805816 {" map" , [](const func_args & args) -> value {
806- args.ensure_count (2 , 3 );
817+ args.ensure_count (2 );
807818 if (!is_val<value_array>(args.get_pos (0 ))) {
808819 throw raised_exception (" map: first argument must be an array" );
809820 }
810- value attribute = args.get_kwarg_or_pos (" attribute" , 1 );
811- if (is_val<value_int>(attribute)) {
812- throw not_implemented_exception (" map: integer attribute not implemented" );
821+ if (!is_val<value_kwarg>(args.get_args ().at (1 ))) {
822+ throw not_implemented_exception (" map: filter-mapping not implemented" );
813823 }
814- if (!is_val<value_string>(attribute)) {
824+ value attribute = args.get_kwarg_or_pos (" attribute" , 1 );
825+ const bool attr_is_int = is_val<value_int>(attribute);
826+ if (!is_val<value_string>(attribute) && !attr_is_int) {
815827 throw raised_exception (" map: attribute must be string or integer" );
816828 }
817- std::string attr_name = attribute->as_string ().str ();
829+ const int64_t attr_int = attr_is_int ? attribute->as_int () : 0 ;
830+ const std::string attr_name = attribute->as_string ().str ();
818831 value default_val = args.get_kwarg (" default" , mk_val<value_undefined>());
819832 auto out = mk_val<value_array>();
820833 auto arr = args.get_pos (0 )->as_array ();
821834 for (const auto & item : arr) {
822- if (!is_val<value_object>(item)) {
823- throw raised_exception (" map: item is not an object" );
835+ value attr_val;
836+ if (attr_is_int) {
837+ attr_val = is_val<value_array>(item) ? item->at (attr_int, default_val) : default_val;
838+ } else {
839+ attr_val = is_val<value_object>(item) ? item->at (attr_name, default_val) : default_val;
824840 }
825- value attr_val = item->at (attr_name, default_val);
826841 out->push_back (attr_val);
827842 }
828843 return out;
@@ -848,29 +863,35 @@ const func_builtins & value_array_t::get_builtins() const {
848863 return arr_editable->pop_at (index);
849864 }},
850865 {" sort" , [](const func_args & args) -> value {
851- args.ensure_count (1 , 3 );
866+ args.ensure_count (1 , 4 );
852867 if (!is_val<value_array>(args.get_pos (0 ))) {
853868 throw raised_exception (" sort: first argument must be an array" );
854869 }
855- bool reverse = args.get_kwarg (" reverse" , mk_val<value_undefined>())->as_bool ();
856- value attribute = args.get_kwarg (" attribute" , mk_val<value_undefined>());
857- std::string attr = attribute->is_undefined () ? " " : attribute->as_string ().str ();
870+ value val_reverse = args.get_kwarg_or_pos (" reverse" , 1 );
871+ value val_case = args.get_kwarg_or_pos (" case_sensitive" , 2 );
872+ value attribute = args.get_kwarg_or_pos (" attribute" , 3 );
873+ // FIXME: sorting is currently always case sensitive
874+ // const bool case_sensitive = val_case->as_bool(); // undefined == false
875+ const bool reverse = val_reverse->as_bool (); // undefined == false
876+ const bool attr_is_int = is_val<value_int>(attribute);
877+ const int64_t attr_int = attr_is_int ? attribute->as_int () : 0 ;
878+ const std::string attr_name = attribute->is_undefined () ? " " : attribute->as_string ().str ();
858879 std::vector<value> arr = cast_val<value_array>(args.get_pos (0 ))->as_array (); // copy
859880 std::sort (arr.begin (), arr.end (),[&](const value & a, const value & b) {
860881 value val_a = a;
861882 value val_b = b;
862883 if (!attribute->is_undefined ()) {
863- if (!is_val<value_object>(a) || !is_val<value_object>(b)) {
864- throw raised_exception (" sort: items are not objects" );
884+ if (attr_is_int && is_val<value_array>(a) && is_val<value_array>(b)) {
885+ val_a = a->at (attr_int);
886+ val_b = b->at (attr_int);
887+ } else if (!attr_is_int && !attr_name.empty () && is_val<value_object>(a) && is_val<value_object>(b)) {
888+ val_a = a->at (attr_name);
889+ val_b = b->at (attr_name);
890+ } else {
891+ throw raised_exception (" sort: unsupported object attribute comparison" );
865892 }
866- val_a = attr.empty () ? a : a->at (attr);
867- val_b = attr.empty () ? b : b->at (attr);
868- }
869- if (reverse) {
870- return value_compare (val_a, val_b, value_compare_op::gt);
871- } else {
872- return !value_compare (val_a, val_b, value_compare_op::gt);
873893 }
894+ return value_compare (val_a, val_b, reverse ? value_compare_op::gt : value_compare_op::lt);
874895 });
875896 return mk_val<value_array>(arr);
876897 }},
@@ -964,7 +985,7 @@ const func_builtins & value_object_t::get_builtins() const {
964985 value val_case = args.get_kwarg_or_pos (" case_sensitive" , 1 );
965986 value val_by = args.get_kwarg_or_pos (" by" , 2 );
966987 value val_reverse = args.get_kwarg_or_pos (" reverse" , 3 );
967- // FIXME: sorting is case sensitive
988+ // FIXME: sorting is currently always case sensitive
968989 // const bool case_sensitive = val_case->as_bool(); // undefined == false
969990 const bool reverse = val_reverse->as_bool (); // undefined == false
970991 if (!val_by->is_undefined ()) {
0 commit comments