Skip to content

[Feature] OrderedDict.move_to_end #8234

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 105 additions & 1 deletion py/objdict.c
Original file line number Diff line number Diff line change
Expand Up @@ -618,14 +618,118 @@ const mp_obj_type_t mp_type_dict = {
};

#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
/* ------------------------------------------------------------------
* Only real difference between OrderedDict and regular dict is that
* ordered has `move_to_end`. This method is only added if asked
* for (due to flash constrains), thus we can just alias the locals'
* table to the one for dicts one when the method is not present.
* ------------------------------------------------------------------*/
#if !MICROPY_CPYTHON_COMPAT
#define ordered_dict_locals_dict dict_locals_dict
#else // !MICROPY_CPYTHON_COMPAT
//| def move_to_end(
//| key: Any,
//| last: Optional[bool] = True
//| ):
//| """
//| :param key: The key of the element to be moved.
//| :param last: Whether it moves to the start(False) or end(default behaviour) of the dict.
//| :raises KeyError: If key is not found on the dict.
//| """
//| ...
//|

STATIC mp_obj_t dict_move_to_end(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
// parse args
enum { ARG_self, ARG_key, ARG_last };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE } },
{ MP_QSTR_last, MP_ARG_BOOL, {.u_bool = true } }
};
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);

mp_obj_dict_t *self = MP_OBJ_TO_PTR(args[ARG_self].u_obj);
mp_obj_t *key = args[ARG_key].u_obj;
bool last = args[ARG_last].u_bool;

const size_t used = self->map.used;
if (!used) {
// can not find key, if there are no keys
mp_raise_type_arg(&mp_type_KeyError, key);
}

// iterate in the desired order (last=True goes 0 -> used)
const size_t last_index = used - 1;
const size_t start = last ? 0 : last_index;
const size_t end = last ? last_index : 0;
const int8_t increment = last ? 1 : -1;

// only swap when needed (after finding the key)
bool found = false;
for (size_t i = start; i != end; i += increment) {
mp_map_elem_t curr = self->map.table[i];

if (!found && mp_obj_equal(curr.key, key)) {
found = true;
}

// shift elements
if (found) {
size_t next = i + increment;

// temporary copy
mp_map_elem_t temp = self->map.table[next];

// next = curr
self->map.table[next] = curr;

// curr = next
self->map.table[i] = temp;
}
}

if (!found) {
mp_raise_type_arg(&mp_type_KeyError, key);
}

return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(dict_move_to_end_obj, 1, dict_move_to_end);

STATIC const mp_rom_map_elem_t ordered_dict_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&dict_clear_obj) },
{ MP_ROM_QSTR(MP_QSTR_copy), MP_ROM_PTR(&dict_copy_obj) },
#if MICROPY_PY_BUILTINS_DICT_FROMKEYS
{ MP_ROM_QSTR(MP_QSTR_fromkeys), MP_ROM_PTR(&dict_fromkeys_obj) },
#endif
{ MP_ROM_QSTR(MP_QSTR_get), MP_ROM_PTR(&dict_get_obj) },
{ MP_ROM_QSTR(MP_QSTR_items), MP_ROM_PTR(&dict_items_obj) },
{ MP_ROM_QSTR(MP_QSTR_keys), MP_ROM_PTR(&dict_keys_obj) },
{ MP_ROM_QSTR(MP_QSTR_pop), MP_ROM_PTR(&dict_pop_obj) },
{ MP_ROM_QSTR(MP_QSTR_popitem), MP_ROM_PTR(&dict_popitem_obj) },
{ MP_ROM_QSTR(MP_QSTR_setdefault), MP_ROM_PTR(&dict_setdefault_obj) },
{ MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&dict_update_obj) },
{ MP_ROM_QSTR(MP_QSTR_values), MP_ROM_PTR(&dict_values_obj) },
{ MP_ROM_QSTR(MP_QSTR_move_to_end), MP_ROM_PTR(&dict_move_to_end_obj) },
{ MP_ROM_QSTR(MP_QSTR___delitem__), MP_ROM_PTR(&mp_op_delitem_obj) },
{ MP_ROM_QSTR(MP_QSTR___getitem__), MP_ROM_PTR(&mp_op_getitem_obj) },
{ MP_ROM_QSTR(MP_QSTR___setitem__), MP_ROM_PTR(&mp_op_setitem_obj) },
{ MP_ROM_QSTR(MP_QSTR___delitem__), MP_ROM_PTR(&mp_op_delitem_obj) },
};

STATIC MP_DEFINE_CONST_DICT(ordered_dict_locals_dict, ordered_dict_locals_dict_table);
#endif // !MICROPY_CPYTHON_COMPAT

const mp_obj_type_t mp_type_ordereddict = {
{ &mp_type_type },
.flags = MP_TYPE_FLAG_EXTENDED,
.name = MP_QSTR_OrderedDict,
.print = dict_print,
.make_new = mp_obj_dict_make_new,
.parent = &mp_type_dict,
.locals_dict = (mp_obj_dict_t *)&dict_locals_dict,
.locals_dict = (mp_obj_dict_t *)&ordered_dict_locals_dict,
MP_TYPE_EXTENDED_FIELDS(
.unary_op = dict_unary_op,
.binary_op = dict_binary_op,
Expand Down