| 178 | |
| 179 | template<bool is_reject> |
| 180 | static value selectattr(const func_args & args) { |
| 181 | args.ensure_count(2, 4); |
| 182 | args.ensure_vals<value_array, value_string, value_string, value_string>(true, true, false, false); |
| 183 | |
| 184 | auto arr = args.get_pos(0)->as_array(); |
| 185 | auto attribute = args.get_pos(1); |
| 186 | auto out = mk_val<value_array>(); |
| 187 | value val_default = mk_val<value_undefined>(); |
| 188 | |
| 189 | if (args.count() == 2) { |
| 190 | // example: array | selectattr("active") |
| 191 | for (const auto & item : arr) { |
| 192 | if (!is_val<value_object>(item)) { |
| 193 | throw raised_exception("selectattr: item is not an object"); |
| 194 | } |
| 195 | value attr_val = item->at(attribute, val_default); |
| 196 | bool is_selected = attr_val->as_bool(); |
| 197 | if constexpr (is_reject) is_selected = !is_selected; |
| 198 | if (is_selected) out->push_back(item); |
| 199 | } |
| 200 | return out; |
| 201 | |
| 202 | } else if (args.count() == 3) { |
| 203 | // example: array | selectattr("equalto", "text") |
| 204 | // translated to: test_is_equalto(item, "text") |
| 205 | std::string test_name = args.get_pos(1)->as_string().str(); |
| 206 | value test_val = args.get_pos(2); |
| 207 | auto & builtins = global_builtins(); |
| 208 | auto it = builtins.find("test_is_" + test_name); |
| 209 | if (it == builtins.end()) { |
| 210 | throw raised_exception("selectattr: unknown test '" + test_name + "'"); |
| 211 | } |
| 212 | auto test_fn = it->second; |
| 213 | for (const auto & item : arr) { |
| 214 | func_args test_args(args.ctx); |
| 215 | test_args.push_back(item); // current object |
| 216 | test_args.push_back(test_val); // extra argument |
| 217 | value test_result = test_fn(test_args); |
| 218 | bool is_selected = test_result->as_bool(); |
| 219 | if constexpr (is_reject) is_selected = !is_selected; |
| 220 | if (is_selected) out->push_back(item); |
| 221 | } |
| 222 | return out; |
| 223 | |
| 224 | } else if (args.count() == 4) { |
| 225 | // example: array | selectattr("status", "equalto", "active") |
| 226 | // translated to: test_is_equalto(item.status, "active") |
| 227 | std::string test_name = args.get_pos(2)->as_string().str(); |
| 228 | auto extra_arg = args.get_pos(3); |
| 229 | auto & builtins = global_builtins(); |
| 230 | auto it = builtins.find("test_is_" + test_name); |
| 231 | if (it == builtins.end()) { |
| 232 | throw raised_exception("selectattr: unknown test '" + test_name + "'"); |
| 233 | } |
| 234 | auto test_fn = it->second; |
| 235 | for (const auto & item : arr) { |
| 236 | if (!is_val<value_object>(item)) { |
| 237 | throw raised_exception("selectattr: item is not an object"); |