After calling BaseContainer.Sort(), the stored values are lost
-
Hi,
My requirement is to rearrange the basecontainer in order of id, so I used the BaseContainer().Sort() function. However, there were two issues in the simple test:- the stored values were lost
- it was not arranged in order of id
import c4d doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def main() -> None: bc = c4d.BaseContainer() for i in range(4,12): bc.SetInt32(i,i) for i in range(3): bc.SetInt32(i,i) for id,v in bc: print(id,v) print("--------") bc.Sort() for id,v in bc: print(id,v) if __name__ == '__main__': main()Did I misunderstand this function?
-
Hey @chuanzhen,
Thank you for reporting this. Yes, you are misunderstanding this function a bit. But I have to admit that I did too first, because the function is very weird in what it does. But when you look at the C++ docs, there is a warning.

I have just updated the Python docs for the next release to also contain the warning. So, this function is effectively only useable for containers holding strings. As you can see by my example below, it does not only remove all values but turns them into strings (and then leaves them empty when the source was not a string). The function is badly named.
Find also a little example below to do what you want: sort a container by values.
Cheers,
FerdinandResult
Root (None , id = -1): ├── 4 (DTYPE_LONG): 4 ├── 5 (DTYPE_LONG): 5 ├── 6 (DTYPE_LONG): 6 ├── 7 (DTYPE_LONG): 7 ├── 8 (DTYPE_LONG): 8 ├── 9 (DTYPE_LONG): 9 ├── 10 (DTYPE_LONG): 10 ├── 11 (DTYPE_LONG): 11 ├── 0 (DTYPE_LONG): 0 ├── 1 (DTYPE_LONG): 1 └── 2 (DTYPE_LONG): 2 -------- Root (None , id = -1): ├── 1 (DTYPE_STRING): ├── 4 (DTYPE_STRING): ├── 5 (DTYPE_STRING): ├── 6 (DTYPE_STRING): ├── 7 (DTYPE_STRING): ├── 8 (DTYPE_STRING): ├── 9 (DTYPE_STRING): ├── 10 (DTYPE_STRING): ├── 11 (DTYPE_STRING): ├── 0 (DTYPE_STRING): └── 2 (DTYPE_STRING):Code
import c4d import mxutils doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def main() -> None: bc = c4d.BaseContainer() for i in range(4,12): bc.SetInt32(i,i) for i in range(3): bc.SetInt32(i,i) print(mxutils.GetContainerTreeString(bc)) print("--------") bc.Sort() print(mxutils.GetContainerTreeString(bc)) if __name__ == '__main__': main()Custom Sorting Function
Result
Root (None , id = -1): ├── 0 (DTYPE_LONG): 0 ├── 1 (DTYPE_LONG): 1 ├── 2 (DTYPE_LONG): 2 ├── 3 (DTYPE_LONG): 3 ├── 5 (DTYPE_LONG): 5 ├── 6 (DTYPE_LONG): 6 ├── 7 (DTYPE_LONG): 7 ├── 8 (DTYPE_LONG): 8 ├── 9 (DTYPE_LONG): 9 ├── 10 (DTYPE_LONG): 10 ├── 11 (DTYPE_LONG): 11 ├── 12 (DTYPE_LONG): 12 ├── 13 (DTYPE_LONG): 13 ├── 14 (DTYPE_LONG): 14 ├── 15 (DTYPE_LONG): 15 ├── 16 (DTYPE_LONG): 16 ├── 17 (DTYPE_LONG): 17 ├── 18 (DTYPE_LONG): 18 ├── 19 (DTYPE_LONG): 19 └── 4 (DTYPE_SUBCONTAINER , id = -1): ├── 0 (DTYPE_LONG): 0 ├── 1 (DTYPE_LONG): 1 ├── 2 (DTYPE_LONG): 2 ├── 3 (DTYPE_LONG): 3 ├── 4 (DTYPE_LONG): 4 ├── 5 (DTYPE_LONG): 5 ├── 6 (DTYPE_LONG): 6 ├── 7 (DTYPE_LONG): 7 ├── 8 (DTYPE_LONG): 8 ├── 9 (DTYPE_LONG): 9 ├── 10 (DTYPE_LONG): 10 ├── 11 (DTYPE_LONG): 11 ├── 12 (DTYPE_LONG): 12 ├── 13 (DTYPE_LONG): 13 ├── 14 (DTYPE_LONG): 14 ├── 15 (DTYPE_LONG): 15 ├── 16 (DTYPE_LONG): 16 ├── 17 (DTYPE_LONG): 17 ├── 18 (DTYPE_LONG): 18 └── 19 (DTYPE_LONG): 19Code
import c4d import mxutils import random doc: c4d.documents.BaseDocument # The currently active document. op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`. def SortContainer(bc: c4d.BaseContainer, mode: str = "value") -> None: """Sorts the given container by ID or value. This function reallocates sub-containers but modifies the passed in container in place. It would also be possible to modify the sub-container in place, but more work to implement and also more complex to run. Due to the fact that we have to copy container data, this is also not the cheapest function. """ def copy(bc: c4d.BaseContainer) -> list[tuple[int, any]]: """Copies the contents of the given BaseContainer to a list of (ID, value) tuples. """ result = [] for i, v in bc: if isinstance(v, c4d.BaseContainer): v = copy(v) result.append((i, v)) return result def build(bc: c4d.BaseContainer, items: list[tuple[int, any]]) -> None: """Builds a BaseContainer from the given list of (ID, value) tuples. """ bc.FlushAll() for i, v in items: if isinstance(v, list): sub = c4d.BaseContainer() build(sub, v) v = sub bc[i] = v def sort_items(items: list[tuple[int, any]], mode: str) -> list[tuple[int, any]]: """Sorts the given list of (ID, value) tuples by ID or value. Nested containers are sorted recursively. """ def is_nested(item: tuple[int, any]) -> bool: return isinstance(item[1], (list, tuple)) def sort_key(item: tuple[int, any]) -> tuple[bool, any]: item_id, value = item # The first key puts nested values after scalar values. # The second key is only compared within the same group. if is_nested(item): return True, item_id if mode == "id" else 0 return False, item_id if mode == "id" else value # Sort nested contents recursively before sorting this level. for index, (item_id, value) in enumerate(items): if is_nested((item_id, value)): items[index] = (item_id, sort_items(value, mode)) return sorted(items, key=sort_key) items: list[tuple[int, any]] = copy(bc) return build(bc, sort_items(items, mode)) def main() -> None: """ """ bc: c4d.BaseContainer = c4d.BaseContainer() data: list[int] = list(range(20)) random.shuffle(data) for v in data: bc[v] = v copy: c4d.BaseContainer = bc.GetClone(0) bc[4] = copy print(mxutils.GetContainerTreeString(bc)) print("--------") SortContainer(bc) print(mxutils.GetContainerTreeString(bc)) if __name__ == '__main__': main() -
F ferdinand moved this topic from Cinema 4D SDK
-
F ferdinand moved this topic from Bugs
-
@ferdinand Thanks for your detailed explanation and the implementation of the sorting function. It's very helpful!