From 224cd48f178bd3682dad7973d4c7c45d4e00f463 Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Fri, 30 Oct 2026 13:03:11 +0100 Subject: [PATCH 1/2] dict filter --- CHANGELOG.md | 3 +- inc/doc.h | 1 + inc/doc.inline.h | 1 + inc/ti/fn/fneach.h | 5 --- inc/ti/fn/fnfilter.h | 67 ++++++++++++++++++++++++++++++ inc/ti/version.h | 2 +- itest/test_collection_functions.py | 20 ++++++++- 7 files changed, 90 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2471974e4..9760f74fa 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 3a618eee6..bce4013c3 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 5dcefb69f..e6c83580a 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 a9fb5ef90..afad72853 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 c8041a734..587622663 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 de3c645f8..1531b62d9 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 c1d0147ac..30a14c6d1 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 #470 + res = await client.query("""//ti + .people.filter(|k, v| v.age == 5); + """) + self.assertEqual(len(res), 1) + self.assertEqual(res[0][0], 'Cato') + + # pr #470 + 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''' From bc8ea57d563976dafb568d5142324c1b0a91082a Mon Sep 17 00:00:00 2001 From: Jeroen van der Heijden Date: Fri, 30 Oct 2026 13:08:40 +0100 Subject: [PATCH 2/2] correct pr --- itest/test_collection_functions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/itest/test_collection_functions.py b/itest/test_collection_functions.py index 30a14c6d1..e4ded5527 100755 --- a/itest/test_collection_functions.py +++ b/itest/test_collection_functions.py @@ -1070,14 +1070,14 @@ async def test_filter(self, client): '.girls.filter(|_v, i|(i == .cato.id()))'))[0]['age'], 5) - # pr #470 + # 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 #470 + # pr #471 res = await client.query("""//ti .people.filter(|k, v| k == 'Iris'); """)