|
13 | 13 | # limitations under the License. |
14 | 14 | import operator |
15 | 15 | from abc import ABC |
| 16 | +from collections import OrderedDict |
16 | 17 | from collections.abc import Mapping, Sequence |
17 | 18 | from copy import copy |
18 | 19 | from functools import partial |
@@ -84,24 +85,24 @@ def apply_to_collection( |
84 | 85 | Returns: |
85 | 86 | The resulting collection |
86 | 87 | """ |
87 | | - |
88 | 88 | # Breaking condition |
89 | 89 | if isinstance(data, dtype) and (wrong_dtype is None or not isinstance(data, wrong_dtype)): |
90 | 90 | return function(data, *args, **kwargs) |
91 | 91 |
|
| 92 | + elem_type = type(data) |
| 93 | + |
92 | 94 | # Recursively apply to collection items |
93 | 95 | if isinstance(data, Mapping): |
94 | | - out = {} |
| 96 | + out = [] # can't use dict, need to preserve order if `OrderedDict` |
95 | 97 | for k, v in data.items(): |
96 | 98 | v = apply_to_collection(v, dtype, function, *args, wrong_dtype=wrong_dtype, **kwargs) |
97 | 99 | if include_none or v is not None: |
98 | | - out[k] = v |
99 | | - return out |
| 100 | + out.append((k, v)) |
| 101 | + return elem_type(out) |
100 | 102 |
|
101 | 103 | is_namedtuple = _is_namedtuple(data) |
102 | 104 | is_sequence = isinstance(data, Sequence) and not isinstance(data, str) |
103 | 105 | if is_namedtuple or is_sequence: |
104 | | - elem_type = type(data) |
105 | 106 | out = [] |
106 | 107 | for d in data: |
107 | 108 | v = apply_to_collection(d, dtype, function, *args, wrong_dtype=wrong_dtype, **kwargs) |
@@ -142,22 +143,23 @@ def apply_to_collections( |
142 | 143 | # in case they were passed reversed |
143 | 144 | data1, data2 = data2, None |
144 | 145 |
|
| 146 | + elem_type = type(data1) |
| 147 | + |
145 | 148 | if isinstance(data1, dtype) and data2 is not None and (wrong_dtype is None or not isinstance(data1, wrong_dtype)): |
146 | 149 | return function(data1, data2, *args, **kwargs) |
147 | 150 |
|
148 | 151 | if isinstance(data1, Mapping) and data2 is not None: |
149 | 152 | # use union because we want to fail if a key does not exist in both |
150 | 153 | zipped = {k: (data1[k], data2[k]) for k in data1.keys() | data2.keys()} |
151 | | - return { |
| 154 | + return elem_type({ |
152 | 155 | k: apply_to_collections(*v, dtype, function, *args, wrong_dtype=wrong_dtype, **kwargs) |
153 | 156 | for k, v in zipped.items() |
154 | | - } |
| 157 | + }) |
155 | 158 |
|
156 | 159 | is_namedtuple = _is_namedtuple(data1) |
157 | 160 | is_sequence = isinstance(data1, Sequence) and not isinstance(data1, str) |
158 | 161 | if (is_namedtuple or is_sequence) and data2 is not None: |
159 | 162 | assert len(data1) == len(data2), 'Sequence collections have different sizes' |
160 | | - elem_type = type(data1) |
161 | 163 | out = [ |
162 | 164 | apply_to_collections(v1, v2, dtype, function, *args, wrong_dtype=wrong_dtype, **kwargs) |
163 | 165 | for v1, v2 in zip(data1, data2) |
|
0 commit comments