Passed
Push — master ( 3318af...ce1cbc )
by Fabio
01:13
created

benedict.utils.dict_util.swap()   A

Complexity

Conditions 2

Size

Total Lines 4
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 2
nop 3
1
# -*- coding: utf-8 -*-
2
3
from benedict.utils import keylist_util
4
5
from six import string_types, text_type
6
from slugify import slugify
7
8
import copy
9
import json
10
import re
11
12
13
def clean(d, strings=True, dicts=True, lists=True):
14
    keys = list(d.keys())
15
    for key in keys:
16
        value = d.get(key, None)
17
        if not value:
18
            del_none = value is None
19
            del_string = strings and isinstance(value, string_types)
20
            del_dict = dicts and isinstance(value, dict)
21
            del_list = lists and isinstance(value, (list, set, tuple, ))
22
            if any([del_none, del_string, del_dict, del_list]):
23
                del d[key]
24
25
26
def clone(d):
27
    return copy.deepcopy(d)
28
29
30
def dump(data):
31
    def encoder(obj):
32
        json_types = (bool, dict, float, int, list, tuple, ) + string_types
33
        if not isinstance(obj, json_types):
34
            return str(obj)
35
    return json.dumps(data, indent=4, sort_keys=True, default=encoder)
36
37
38
def filter(d, predicate):
39
    if not callable(predicate):
40
        raise ValueError('predicate argument must be a callable.')
41
    new_dict = d.copy()
42
    new_dict.clear()
43
    keys = list(d.keys())
44
    for key in keys:
45
        value = d.get(key, None)
46
        if predicate(key, value):
47
            new_dict[key] = value
48
    return new_dict
49
50
51
def flatten(d, separator='_', **kwargs):
52
    new_dict = d.copy()
53
    new_dict.clear()
54
    keys = list(d.keys())
55
    base_key = kwargs.pop('base_key', '')
56
    for key in keys:
57
        new_key = '{}{}{}'.format(
58
            base_key, separator, key) if base_key and separator else key
59
        value = d.get(key, None)
60
        if isinstance(value, dict):
61
            new_value = flatten(value, separator=separator, base_key=new_key)
62
            new_value.update(new_dict)
63
            new_dict.update(new_value)
64
        else:
65
            new_dict[new_key] = value
66
    return new_dict
67
68
69
def invert(d, flat=False):
70
    new_dict = d.copy()
71
    new_dict.clear()
72
    for key, value in d.items():
73
        if flat:
74
            new_dict.setdefault(value, key)
75
        else:
76
            new_dict.setdefault(value, []).append(key)
77
    return new_dict
78
79
80
def items_sorted_by(d, key, reverse=False):
81
    return sorted(d.items(), key=key, reverse=reverse)
82
83
84
def items_sorted_by_keys(d, reverse=False):
85
    return items_sorted_by(d, key=lambda item: item[0], reverse=reverse)
86
87
88
def items_sorted_by_values(d, reverse=False):
89
    return items_sorted_by(d, key=lambda item: item[1], reverse=reverse)
90
91
92
def keypaths(d, separator='.'):
93
    if not separator or not isinstance(separator, string_types):
94
        raise ValueError('separator argument must be a (non-empty) string.')
95
96
    def f(parent, parent_keys):
97
        kp = []
98
        for key, value in parent.items():
99
            keys = parent_keys + [key]
100
            kp += [separator.join(text_type(k) for k in keys)]
101
            if isinstance(value, dict):
102
                kp += f(value, keys)
103
        return kp
104
    kp = f(d, [])
105
    kp.sort()
106
    return kp
107
108
109
def merge(d, other, *args):
110
    others = [other] + list(args)
111
    for other in others:
112
        for key, value in other.items():
113
            src = d.get(key, None)
114
            if isinstance(src, dict) and isinstance(value, dict):
115
                merge(src, value)
116
            else:
117
                d[key] = value
118
    return d
119
120
121
def move(d, key_src, key_dest, overwrite=True):
122
    if key_dest == key_src:
123
        return
124
    if key_dest in d and not overwrite:
125
        raise KeyError
126
    d[key_dest] = d.pop(key_src)
127
128
129
def remove(d, keys, *args):
130
    if isinstance(keys, string_types):
131
        keys = [keys]
132
    keys += args
133
    for key in keys:
134
        d.pop(key, None)
135
136
137
def rename(d, key, key_new):
138
    move(d, key, key_new, overwrite=False)
139
140
141
def search(d, query,
142
           in_keys=True, in_values=True, exact=False, case_sensitive=True):
143
    items = []
144
145
    def get_term(value):
146
        v_is_str = isinstance(value, string_types)
147
        v = value.lower() if (v_is_str and not case_sensitive) else value
148
        return (v, v_is_str, )
149
150
    q, q_is_str = get_term(query)
151
152
    def get_match(cond, value):
153
        if not cond:
154
            return False
155
        v, v_is_str = get_term(value)
156
        # TODO: add regex support
157
        if exact:
158
            return q == v
159
        elif q_is_str and v_is_str:
160
            return q in v
161
        return False
162
163
    def f(item_dict, item_key, item_value):
164
        if get_match(in_keys, item_key) or get_match(in_values, item_value):
165
            items.append((item_dict, item_key, item_value, ))
166
    traverse(d, f)
167
    return items
168
169
170
def standardize(d):
171
    def f(item_dict, item_key, item_value):
172
        if isinstance(item_key, string_types):
173
            # https://stackoverflow.com/a/12867228/2096218
174
            norm_key = re.sub(
175
                r'((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))', r'_\1', item_key)
176
            norm_key = slugify(norm_key, separator='_')
177
            move(item_dict, item_key, norm_key)
178
    traverse(d, f)
179
180
181
def subset(d, keys, *args):
182
    new_dict = d.copy()
183
    new_dict.clear()
184
    if isinstance(keys, string_types):
185
        keys = [keys]
186
    keys += args
187
    for key in keys:
188
        new_dict[key] = d.get(key, None)
189
    return new_dict
190
191
192
def swap(d, key1, key2):
193
    if key1 == key2:
194
        return
195
    d[key1], d[key2] = d[key2], d[key1]
196
197
198
def traverse(d, callback):
199
    if not callable(callback):
200
        raise ValueError('callback argument must be a callable.')
201
    keys = list(d.keys())
202
    for key in keys:
203
        value = d.get(key, None)
204
        callback(d, key, value)
205
        if isinstance(value, dict):
206
            traverse(value, callback)
207
208
209
def unflatten(d, separator='_'):
210
    new_dict = d.copy()
211
    new_dict.clear()
212
    new_dict_cursor = new_dict
213
    keys = list(d.keys())
214
    for key in keys:
215
        value = d.get(key, None)
216
        new_value = unflatten(value, separator=separator) if isinstance(
217
            value, dict) else value
218
        new_keys = key.split(separator)
219
        keylist_util.set_item(new_dict, new_keys, new_value)
220
    return new_dict
221
222
223
def unique(d):
224
    values = []
225
    keys = list(d.keys())
226
    for key in keys:
227
        value = d.get(key, None)
228
        if value in values:
229
            d.pop(key, None)
230
            continue
231
        values.append(value)
232