diff --git a/CHANGELOG.md b/CHANGELOG.md index 2471974e..9760f74f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ -# v1.10.4-alpha0 +# v1.10.4-alpha1 * Add support for marking properties as read-only, pr #470. +* Added `dict.filter(..)` function, pr #471. # v1.10.3 diff --git a/inc/doc.h b/inc/doc.h index 3a618eee..bce4013c 100644 --- a/inc/doc.h +++ b/inc/doc.h @@ -226,6 +226,7 @@ #define DOC_DICT_DEL DOC_SEE("data-types/dict/del") #define DOC_DICT_DUP DOC_SEE("data-types/dict/dup") #define DOC_DICT_EACH DOC_SEE("data-types/dict/each") +#define DOC_DICT_FILTER DOC_SEE("data-types/dict/filter") #define DOC_DICT_GET DOC_SEE("data-types/dict/get") #define DOC_DICT_HAS DOC_SEE("data-types/dict/has") #define DOC_DICT_KEYS DOC_SEE("data-types/dict/keys") diff --git a/inc/doc.inline.h b/inc/doc.inline.h index 5dcefb69..e6c83580 100644 --- a/inc/doc.inline.h +++ b/inc/doc.inline.h @@ -42,6 +42,7 @@ static inline const char * doc_filter(ti_val_t * val) case TI_VAL_THING: return DOC_THING_FILTER; case TI_VAL_ARR: return DOC_LIST_FILTER; case TI_VAL_SET: return DOC_SET_FILTER; + case TI_VAL_DICT: return DOC_DICT_FILTER; default: return NULL; } } diff --git a/inc/ti/fn/fneach.h b/inc/ti/fn/fneach.h index a9fb5ef9..afad7285 100644 --- a/inc/ti/fn/fneach.h +++ b/inc/ti/fn/fneach.h @@ -172,7 +172,6 @@ static int do__f_each(ti_query_t * query, cleri_node_t * nd, ex_t * e) .closure = closure, .query = query, }; - ; if (ti_closure_arg_used(closure, 0) ? ti_dict_items( @@ -183,11 +182,7 @@ static int do__f_each(ti_query_t * query, cleri_node_t * nd, ex_t * e) (ti_dict_t *) iterval, (ti_dict_cb) each__walk_dict_values, &w)) - { - if (!e->nr) - ex_set_mem(e); goto fail2; - } break; } } diff --git a/inc/ti/fn/fnfilter.h b/inc/ti/fn/fnfilter.h index c8041a73..58762266 100644 --- a/inc/ti/fn/fnfilter.h +++ b/inc/ti/fn/fnfilter.h @@ -49,6 +49,46 @@ static int filter__walk_i(ti_item_t * item, filter__walk_i_t * w) return 0; } +typedef struct +{ + ex_t * e; + ti_closure_t * closure; + ti_query_t * query; + ti_dict_t * dict; +} filter__walk_dict_t; + +static int filter__walk_dict_items(ti_val_t * k, + ti_val_t * v, + filter__walk_dict_t * w) +{ + ti_closure_vars_dict(w->closure, k, v); + if (ti_closure_do_statement(w->closure, w->query, w->e)) + return -1; + + if (ti_val_as_bool(w->query->rval) && ti_dict_set(w->dict, k, &v, w->e)) + return -1; + + ti_val_unsafe_drop(w->query->rval); + w->query->rval = NULL; + return 0; +} + +static int filter__walk_pair(ti_dict_key_t * k, + ti_val_t * v, + filter__walk_dict_t * w) +{ + ti_closure_vars_dict(w->closure, (ti_val_t *) ti_nil_weak_get(), v); + if (ti_closure_do_statement(w->closure, w->query, w->e)) + return -1; + + if (ti_val_as_bool(w->query->rval) && ti_dict_setr(w->dict, k, v)) + return -1; + + ti_val_unsafe_drop(w->query->rval); + w->query->rval = NULL; + return 0; +} + static int do__f_filter(ti_query_t * query, cleri_node_t * nd, ex_t * e) { const char * doc; @@ -198,6 +238,33 @@ static int do__f_filter(ti_query_t * query, cleri_node_t * nd, ex_t * e) (imap_cb) filter__walk_set, &w)) goto fail2; + break; + } + case TI_VAL_DICT: + { + filter__walk_dict_t w = { + .e = e, + .closure = closure, + .query = query, + .dict = ti_dict_create(), + }; + + if (!w.dict) + goto fail2; + + retval = (ti_val_t *) w.dict; + + if (ti_closure_arg_used(closure, 0) + ? ti_dict_items( + (ti_dict_t *) iterval, + (ti_dict_item_cb) filter__walk_dict_items, + &w) + : ti_dict_pairs( + (ti_dict_t *) iterval, + (ti_dict_pair_cb) filter__walk_pair, + &w)) + goto fail2; + break; } } diff --git a/inc/ti/version.h b/inc/ti/version.h index de3c645f..1531b62d 100644 --- a/inc/ti/version.h +++ b/inc/ti/version.h @@ -25,7 +25,7 @@ * "-rc0" * "" */ -#define TI_VERSION_PRE_RELEASE "-alpha0" +#define TI_VERSION_PRE_RELEASE "-alpha1" #define TI_MAINTAINER \ "Jeroen van der Heijden " diff --git a/itest/test_collection_functions.py b/itest/test_collection_functions.py index c1d0147a..e4ded552 100755 --- a/itest/test_collection_functions.py +++ b/itest/test_collection_functions.py @@ -1015,7 +1015,7 @@ async def test_extend(self, client): await client.query('a = .list; a.extend([123]);') async def test_filter(self, client): - await client.query(r''' + await client.query(r"""//ti .iris = { name: 'Iris', age: 6, @@ -1026,7 +1026,8 @@ async def test_filter(self, client): age: 5, }; .girls = set([.iris, .cato]); - ''') + .people = dict(.girls.map(|v| [v.name, v])); + """) with self.assertRaisesRegex( LookupError, 'type `nil` has no function `filter`'): @@ -1069,11 +1070,26 @@ async def test_filter(self, client): '.girls.filter(|_v, i|(i == .cato.id()))'))[0]['age'], 5) + # pr #471 + res = await client.query("""//ti + .people.filter(|k, v| v.age == 5); + """) + self.assertEqual(len(res), 1) + self.assertEqual(res[0][0], 'Cato') + + # pr #471 + res = await client.query("""//ti + .people.filter(|k, v| k == 'Iris'); + """) + self.assertEqual(len(res), 1) + self.assertEqual(res[0][0], 'Iris') + self.assertEqual(await client.query('.iris.filter(||nil);'), {}) self.assertEqual(await client.query('.iris.likes.filter(||nil);'), []) self.assertEqual(await client.query(r'{}.filter(||true)'), {}) self.assertEqual(await client.query(r'[].filter(||true)'), []) self.assertEqual(await client.query(r'set().filter(||1)'), []) + self.assertEqual(await client.query(r'dict().filter(||1)'), []) async def test_reverse(self, client): await client.query(r'''