1
1
#!/usr/bin/env python
2
2
3
3
4
- def _get_ordered_keys (rows , column_index ):
4
+ def _get_keys (rows , column_index , lowercase = False ):
5
5
"""
6
- Get ordered keys from rows, given the key column index.
6
+ Get keys from rows as keys in a dictionary (i.e. unordered) , given the key column index.
7
7
"""
8
- return [r [column_index ] for r in rows ]
8
+ pairs = ((r [column_index ], True ) for r in rows )
9
+ return CaseInsensitiveDict (pairs ) if lowercase else dict (pairs )
9
10
10
11
11
- def _get_mapped_keys (rows , column_index ):
12
- mapped_keys = {}
12
+ def _get_mapped_keys (rows , column_index , case_insensitive = False ):
13
+ mapped_keys = CaseInsensitiveDict () if case_insensitive else {}
13
14
14
15
for r in rows :
15
16
key = r [column_index ]
@@ -21,6 +22,11 @@ def _get_mapped_keys(rows, column_index):
21
22
22
23
return mapped_keys
23
24
25
+ def _lower (key ):
26
+ """Transforms a string to lowercase, leaves other types alone."""
27
+ keyfn = getattr (key , 'lower' , None )
28
+ return keyfn () if keyfn else key
29
+
24
30
25
31
def sequential_join (left_rows , right_rows , header = True ):
26
32
"""
@@ -49,7 +55,7 @@ def sequential_join(left_rows, right_rows, header=True):
49
55
return output
50
56
51
57
52
- def inner_join (left_rows , left_column_id , right_rows , right_column_id , header = True ):
58
+ def inner_join (left_rows , left_column_id , right_rows , right_column_id , header = True , ignore_case = False ):
53
59
"""
54
60
Execute an inner join on two tables and return the combined table.
55
61
"""
@@ -63,7 +69,7 @@ def inner_join(left_rows, left_column_id, right_rows, right_column_id, header=Tr
63
69
output = []
64
70
65
71
# Map right rows to keys
66
- right_mapped_keys = _get_mapped_keys (right_rows , right_column_id )
72
+ right_mapped_keys = _get_mapped_keys (right_rows , right_column_id , ignore_case )
67
73
68
74
for left_row in left_rows :
69
75
len_left_row = len (left_row )
@@ -80,7 +86,7 @@ def inner_join(left_rows, left_column_id, right_rows, right_column_id, header=Tr
80
86
return output
81
87
82
88
83
- def full_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True ):
89
+ def full_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True , ignore_case = False ):
84
90
"""
85
91
Execute full outer join on two tables and return the combined table.
86
92
"""
@@ -94,11 +100,11 @@ def full_outer_join(left_rows, left_column_id, right_rows, right_column_id, head
94
100
else :
95
101
output = []
96
102
97
- # Get ordered keys
98
- left_ordered_keys = _get_ordered_keys (left_rows , left_column_id )
103
+ # Get left keys
104
+ left_keys = _get_keys (left_rows , left_column_id , ignore_case )
99
105
100
106
# Get mapped keys
101
- right_mapped_keys = _get_mapped_keys (right_rows , right_column_id )
107
+ right_mapped_keys = _get_mapped_keys (right_rows , right_column_id , ignore_case )
102
108
103
109
for left_row in left_rows :
104
110
len_left_row = len (left_row )
@@ -116,13 +122,13 @@ def full_outer_join(left_rows, left_column_id, right_rows, right_column_id, head
116
122
for right_row in right_rows :
117
123
right_key = right_row [right_column_id ]
118
124
119
- if right_key not in left_ordered_keys :
125
+ if right_key not in left_keys :
120
126
output .append (([u'' ] * len_left_headers ) + right_row )
121
127
122
128
return output
123
129
124
130
125
- def left_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True ):
131
+ def left_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True , ignore_case = False ):
126
132
"""
127
133
Execute left outer join on two tables and return the combined table.
128
134
"""
@@ -137,7 +143,7 @@ def left_outer_join(left_rows, left_column_id, right_rows, right_column_id, head
137
143
output = []
138
144
139
145
# Get mapped keys
140
- right_mapped_keys = _get_mapped_keys (right_rows , right_column_id )
146
+ right_mapped_keys = _get_mapped_keys (right_rows , right_column_id , ignore_case )
141
147
142
148
for left_row in left_rows :
143
149
len_left_row = len (left_row )
@@ -155,7 +161,7 @@ def left_outer_join(left_rows, left_column_id, right_rows, right_column_id, head
155
161
return output
156
162
157
163
158
- def right_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True ):
164
+ def right_outer_join (left_rows , left_column_id , right_rows , right_column_id , header = True , ignore_case = False ):
159
165
"""
160
166
Execute right outer join on two tables and return the combined table.
161
167
"""
@@ -168,11 +174,11 @@ def right_outer_join(left_rows, left_column_id, right_rows, right_column_id, hea
168
174
else :
169
175
output = []
170
176
171
- # Get ordered keys
172
- left_ordered_keys = _get_ordered_keys (left_rows , left_column_id )
177
+ # Get left keys
178
+ left_keys = _get_keys (left_rows , left_column_id , ignore_case )
173
179
174
180
# Get mapped keys
175
- right_mapped_keys = _get_mapped_keys (right_rows , right_column_id )
181
+ right_mapped_keys = _get_mapped_keys (right_rows , right_column_id , ignore_case )
176
182
177
183
for left_row in left_rows :
178
184
len_left_row = len (left_row )
@@ -188,7 +194,47 @@ def right_outer_join(left_rows, left_column_id, right_rows, right_column_id, hea
188
194
for right_row in right_rows :
189
195
right_key = right_row [right_column_id ]
190
196
191
- if right_key not in left_ordered_keys :
197
+ if right_key not in left_keys :
192
198
output .append (([u'' ] * len_left_headers ) + right_row )
193
199
194
200
return output
201
+
202
+
203
+
204
+ class CaseInsensitiveDict (dict ):
205
+ """
206
+ Adapted from http://stackoverflow.com/a/32888599/1583437
207
+ """
208
+ def __init__ (self , * args , ** kwargs ):
209
+ super (CaseInsensitiveDict , self ).__init__ (* args , ** kwargs )
210
+ self ._convert_keys ()
211
+
212
+ def __getitem__ (self , key ):
213
+ return super (CaseInsensitiveDict , self ).__getitem__ (_lower (key ))
214
+
215
+ def __setitem__ (self , key , value ):
216
+ super (CaseInsensitiveDict , self ).__setitem__ (_lower (key ), value )
217
+
218
+ def __delitem__ (self , key ):
219
+ return super (CaseInsensitiveDict , self ).__delitem__ (_lower (key ))
220
+
221
+ def __contains__ (self , key ):
222
+ return super (CaseInsensitiveDict , self ).__contains__ (_lower (key ))
223
+
224
+ def pop (self , key , * args , ** kwargs ):
225
+ return super (CaseInsensitiveDict , self ).pop (_lower (key ), * args , ** kwargs )
226
+
227
+ def get (self , key , * args , ** kwargs ):
228
+ return super (CaseInsensitiveDict , self ).get (_lower (key ), * args , ** kwargs )
229
+
230
+ def setdefault (self , key , * args , ** kwargs ):
231
+ return super (CaseInsensitiveDict , self ).setdefault (_lower (key ), * args , ** kwargs )
232
+
233
+ def update (self , single_arg = None , ** kwargs ):
234
+ super (CaseInsensitiveDict , self ).update (self .__class__ (single_arg ))
235
+ super (CaseInsensitiveDict , self ).update (self .__class__ (** kwargs ))
236
+
237
+ def _convert_keys (self ):
238
+ for k in list (self .keys ()):
239
+ v = super (CaseInsensitiveDict , self ).pop (k )
240
+ self .__setitem__ (k , v )
0 commit comments