Find a Syntax error in Python DNA Sequence - python

Im getting a syntax error in line 5 // for k,v in // how can I solve this please?
i'm not sure what's the error here
alt_map = {'ins':'0'}
complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'}
def reverse_complement(seq):
for k,v in
alt_map.iteritems():
seq = seq.replace(k,v)
bases = list(seq)
bases = reversed([complement.get(base,base) for base in bases])
bases = ''.join(bases)
for k,v in
alt_map.iteritems():
bases = bases.replace(v,k) return bases
>>> seq = "TCGGinsGCCC"
>>> print "Reverse Complement:"
>>> print(reverse_complement(seq))
GGGCinsCCGA
def ReverseComplement(Pattern):
revcomp = [ ]
x = len(Pattern)
for i in Pattern:
x = x - 1
revcomp.append(Pattern[x])
return ''.join(revcomp)

Python cannot do automatic line breaks inmidst of a command.
You have to do:
for k,v in \
with \ as explicite sign that you do a linebreak inmidst of a for loop expression.
Python is a language where whitespace is partly part of the syntax.
So you have to be hyper correct in terms of whitespaces/linebreaks.

Changed your code:
alt_map = {'ins': '0'}
complement = {'A': 'T', 'C': 'G', 'G': 'C', 'T': 'A'}
def reverse_complement(seq):
for k, v in alt_map.items():
seq = seq.replace(k, v)
bases = list(seq)
bases = reversed([complement.get(base, base) for base in bases])
bases = ''.join(bases)
for k, v in alt_map.items():
bases = bases.replace(v, k)
return bases
if __name__ == '__main__':
seq = 'TCGGinsGCCC'
print(reverse_complement(seq))
Output:
GGGCinsCCGA

Related

Python DFS nested dictionary

I've written a function which should be able to search a nested dictionary, using DFS, to find a specific value. The recursive element seems to be working fine, however, when the base case should return True, it simply doesn't.
obj = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'i':'j'}]}
def obj_dfs(obj, target):
if type(obj) == dict:
for key, val in obj.items():
obj_dfs(key, target)
obj_dfs(val, target)
elif type(obj) in (list, tuple):
for elem in obj:
obj_dfs(elem, target)
else:
if obj == target:
print(f"{obj} == {target}")
return True
else:
print(f"{obj} != {target}")
return False
obj_dfs(obj, 'j')
And the results. As you can see, the standard output "i==i" shows that this element was evaluated correctly but the return True statement isn't functioning as intended. I've verified that if I call obj_dfs(obj, 'j'), that experiences the same error.
a != j
c != j
d != j
e != j
f != j
b != j
g != j
h != j
i != j
j == j
False
Why is this? And how can I fix this?
As the comments point out, you need to return the results of the recursive calls. Since you are just looking for a True/False match, you can pass the recursive calls into any() which will exit early with True if there is a match. The base case can simple be whether obj == target.
obj = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'i':'j'}]}
def obj_dfs(obj, target):
if obj == target:
return True
if isinstance(obj, dict):
return any(obj_dfs(v, target) for v in obj.items())
elif isinstance(obj, (list, tuple)):
return any(obj_dfs(l, target) for l in obj)
return False
obj_dfs(obj, 'i'), obj_dfs(obj, 'j'), obj_dfs(obj, 'd'), obj_dfs(obj, 'x')
# (True, True, True, False)
This allows for a three simple blocks. Notice we are checking for a tuple as well as a list in the last isinstance. This allows you to simply pass in the dict item()s rather than looping over keys and values independently.
Adding a print(obj) as the first line of the function will show the order in which you are traversing the data. For example obj_dfs(obj, 'j') will print:
{'a': [{'c': 'd'}, {'e': 'f'}], 'b': [{'g': 'h'}, {'i': 'j'}]}
('a', [{'c': 'd'}, {'e': 'f'}])
a
[{'c': 'd'}, {'e': 'f'}]
{'c': 'd'}
('c', 'd')
c
d
{'e': 'f'}
('e', 'f')
e
f
('b', [{'g': 'h'}, {'i': 'j'}])
b
[{'g': 'h'}, {'i': 'j'}]
{'g': 'h'}
('g', 'h')
g
h
{'i': 'j'}
('i', 'j')
i
j
I made some edits to your code
obj = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'i':'j'}]}
def obj_dfs(obj, target):
if type(obj) == dict:
for key, val in obj.items():
if(key==target):
return val
else:
result=obj_dfs(val, target)
if result!=None: return result
elif type(obj) in (list, tuple):
for elem in obj:
result=obj_dfs(elem, target)
if result!=None: return result
else:
if obj==target: return True
print(obj_dfs(obj, 'i'))
I don't know why you would just return true instead of the value though, so I put it that if its a dictionary key it would return the value, instead it would return true, to show that it is found
Expanding on my comment, try this, where we pass return values up the chain and always return True if a child returned True:
obj = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'i':'j'}]}
obj2 = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'g':'j'}]}
def obj_dfs(obj, target):
if type(obj) == dict:
for key, val in obj.items():
keyret = obj_dfs(key, target)
valueret = obj_dfs(val, target)
if keyret is True or valueret is True:
return True
else:
return False
elif type(obj) in (list, tuple):
rets = []
for elem in obj:
rets.append(obj_dfs(elem, target))
if True in rets:
return True
else:
return False
else:
if obj == target:
print(f"{obj} == {target}")
return True
else:
print(f"{obj} != {target}")
return False
print(obj_dfs(obj, 'i'))
print(obj_dfs(obj2, 'i'))
Recursion is a functional heritage and so using it with functional style yields the best results. This means decoupling concerns and pushing side effects to the fringe of your program. obj_dfs performs a depth-first traversal and mixes in search logic. And for purposes of debugging, includes a print side effect.
Decomposition results in functions that are easier to write, test, and reuse in various parts of our program. We'll start with a generic dfs for traversal -
def dfs(t, path = []):
if isinstance(t, dict):
for key, val in t.items():
yield from dfs(val, [*path, key])
elif isinstance(t, (list, tuple)):
for key, val in enumerate(t):
yield from dfs(val, [*path, key])
else:
yield path, t
obj = {'a': [{'c':'d'}, {'e':'f'}],
'b': [{'g':'h'}, {'i':'j'}]}
for path, val in dfs(obj):
print(path, val) # side effect decided by caller
['a', 0, 'c'] d
['a', 1, 'e'] f
['b', 0, 'g'] h
['b', 1, 'i'] j
The suggested solution and other answers here collapse the semantic difference between key and value, providing no differentiation on the particular match of target. Writing dfs as we did above, we can know which part of obj matched.
keys
values
['a', 0, 'c']
d
['a', 1, 'e']
f
['b', 0, 'g']
h
['b', 1, 'i']
j
has_value and has_key are easily defined in terms of dfs -
def has_value(t, target):
for path, val in dfs(t):
if val == target:
return True
return False
def has_key(t, target):
for path, val in dfs(t):
if target in path:
return True
return False
print(has_value(obj, "j")) # True
print(has_key(obj, "j")) # False
print(has_value(obj, "i")) # False
print(has_key(obj, "i")) # True

In python, how best to insert key:value to json, given variable path and value

I need to create a json file , given a dictionary of paths and their values. I written some code for adding an entry, that looks like it functions and result is correct, but as somebody who new in python, I wonder how can this be improved, and if there is a function that does the same, already exist in modules included with python 2.7?
def path_to_list(path):
if isinstance(path, (str,)):
map_list = path.split("/")
for i, key in enumerate(map_list):
if key.isdigit():
map_list[i] = int(key)
else:
map_list = path
return map_list
def add_to_dictionary(dic, keys, value):
for i, key in enumerate(keys[:-1]):
if i < len(keys)-1 and isinstance(keys[i+1], int):
# Case where current key should be a list, since next key is
# is list position
if key not in dic.keys():
# Case list not yet exist
dic[keys[i]] = []
dic[keys[i]].append({})
dic = dic.setdefault(key, {})
elif not isinstance(dic[key], list):
# Case key exist , but not a list
# TO DO : check how to handle
print "Failed to insert " + str(keys) + ", trying to insert multiple to not multiple "
break
else:
# Case where the list exist
dic = dic.setdefault(key, {})
elif i < len(keys)-1 and isinstance(key, int):
# Case where current key is instance number in a list
try:
# If this succeeds instance already exist
dic = dic[key]
except (IndexError,KeyError):
# Case where list exist , but need to add new instances ,
# as key instance not exist
while len(dic)-1 < key:
dic.append({})
dic = dic[key]
else:
# Case where key is not list or instance of list
dic = dic.setdefault(key, {})
# Update value
dic[keys[-1]] = value
my_dict1 ={}
add_to_dictionary(my_dict1, path_to_list("a/0/b/c"), 1)
print my_dict1
{'a': [{'b': {'c': 1}}]}
add_to_dictionary(my_dict1, path_to_list("a/2/b/c"), "string")
print my_dict1
{'a': [{'b': {'c': 1}}, {}, {'b': {'c': 'string'}}]}
add_to_dictionary(my_dict1, path_to_list("a/2/b/c"), "new string")
print my_dict1
{'a': [{'b': {'c': 1}}, {}, {'b': {'c': 'new string'}}]}
Some keys may already exist, then I update just the value.
Numeric keys indicate that key before it can have multiple values, and I'm adding/updating value in this place in array
Here is my implementation of your data structure using nested dictionaries:
class Tree(dict):
'''http://stackoverflow.com/questions/635483/what-is-the-best-way-to-implement-nested-dictionaries-in-python'''
def __missing__(d, k):
v = d[k] = type(d)()
return v
def grow(d, path, v):
ps = map(lambda k: int(k) if k.isdigit() else k, path.split('/'))
reduce(lambda d, k: d[k], ps[:-1], d)[ps[-1]] = v
Testing this:
t = Tree()
t.grow('a/0/b/c', 1)
print t
t['a'][2]['b']['c'] = 'string'
print t
t.grow('a/2/b/c', 'new_string')
print t
gives:
{'a': {0: {'b': {'c': 1}}}}
{'a': {0: {'b': {'c': 1}}, 2: {'b': {'c': 'string'}}}}
{'a': {0: {'b': {'c': 1}}, 2: {'b': {'c': 'new_string'}}}}
But you want the integer indexed dictionaries to be arrays. The routine below steps through the nested dictionaries turning some into lists. It does some copying so as to not mess up the original nested dictionaries. I'd use this as part of the outout stage only.
import numbers
def keys_all_int(d):
return reduce(lambda r, k: r and isinstance(k, numbers.Integral), d.keys(), True)
def listify(d):
'''
Take a tree of nested dictionaries, and
return a copy of the tree with sparse lists in place of the dictionaries
that had only integers as keys.
'''
if isinstance(d, dict):
d = d.copy()
for k in d:
d[k] = listify(d[k])
if keys_all_int(d):
ds = [{}]*(max(d.keys())+1)
for k in d:
ds[k] = d[k]
return ds
return d
Testing this:
t = Tree()
t.grow('a/0/b/c', 1)
print listify(t)
t['a'][2]['b']['c'] = 'string'
print listify(t)
t.grow('a/2/b/c', 'new_string')
print listify(t)
Gives:
{'a': [{'b': {'c': 1}}]}
{'a': [{'b': {'c': 1}}, {}, {'b': {'c': 'string'}}]}
{'a': [{'b': {'c': 1}}, {}, {'b': {'c': 'new_string'}}]}
Finally if you are dealing with JSON use the json module:
import json
print json.dumps(listify(t),
sort_keys=True, indent = 4, separators = (',', ': '))
Gives:
{
"a": [
{
"b": {
"c": 1
}
},
{},
{
"b": {
"c": "new_string"
}
}
]
}

Yen's Algorithm implementation not choosing shortest paths

I am using Yen's Algorithm (Wikipedia) to find k shortest paths in a graph. In the example below, my graph is a dictionary where each node is a key, with its value being the neighbors. Map() from dotmap simply allows for dictionaries to be converted into an object where keys can be accessed with dot notation. I want to find the four shortest paths in descending order from A to F where every edge has equal weight. The first two are ties (A > B > D > F) and (A > E > D > F), and the next two are (A > B > C > G > F) and finally (A > B > D > C > G > F). It is possible that my implementation of Dijkstra's (called AStar despite having no heuristic) is flawed because it is returning an empty list when no path is found. How can I have my code only pick the valid paths? Currently it returns [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], [], []] -- it should return [['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']] which are the shortest paths.
import copy
import heapq
from dotmap import Map
from itertools import count
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': 0})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
graph_clone = copy.deepcopy(graph)
A = [AStar(graph, source, sink).search().path]
B = []
for k in range(1, k_paths):
for i in range(len(A[-1]) - 1):
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
graph_clone = copy.deepcopy(graph)
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
A.append(B[0].path)
B.pop()
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
import copy
import heapq
#from dotmap import Map
from itertools import count
class Map(dict):
def __getattr__(self, k):
return self[k]
def __setattr__(self, k, v):
self[k] = v
graph = {
'A': ['B', 'E'],
'B': ['C', 'D'],
'C': ['G'],
'D': ['C', 'F'],
'E': ['D'],
'F': [],
'G': ['F']
}
class PriorityQueue:
def __init__(self):
self.elements = []
self._counter = count()
def empty(self):
return len(self.elements) == 0
def put(self, item, priority):
heapq.heappush(self.elements, (priority, item,))
def get(self):
return heapq.heappop(self.elements)[1]
class AStar:
def __init__(self, graph, start, goals=[]):
self.graph = graph
self.start = start
self.frontier = PriorityQueue()
self.frontier.put(start, 0)
self.previous = {}
self.previous[start] = None
self.costs = {}
self.costs[start] = 0
self.final_path = None
self.goals = goals
self.goal = None
def search(self):
graph = self.graph
frontier = self.frontier
goals = self.goals
costs = self.costs
while not frontier.empty():
state = frontier.get()
if state in goals:
cost = self.costs[state]
self.goal = state
self.final_path = self.trace_path()
return Map({'path': self.final_path, 'cost': cost})
for next_state in graph[state]:
new_cost = costs[state] + 1
if next_state not in costs or new_cost < costs[next_state]:
costs[next_state] = new_cost
priority = new_cost
frontier.put(next_state, priority)
self.previous[next_state] = state
# No path found
return Map({'path': [], 'cost': float('inf')})
def trace_path(self):
current = self.goal
path = []
while current != self.start:
path.append(current)
current = self.previous[current]
path.append(self.start)
path.reverse()
return path
def YenKSP(graph, source, sink, k_paths):
A = [AStar(graph, source, sink).search().path]
B = []
for _ in range(1, k_paths):
for i in range(len(A[-1]) - 1):
graph_clone = copy.deepcopy(graph)
spur_node = A[-1][i]
root_path = A[-1][:i+1]
for path in A:
if len(path) > i and root_path == path[:i+1]:
if path[i+1] in graph_clone[path[i]]:
graph_clone[path[i]].remove(path[i+1])
result = AStar(graph_clone, spur_node, sink).search()
spur_path = result.path
total_path = root_path[:-1] + spur_path
spur_cost = AStar(graph_clone, source, spur_node).search().cost
B.append(Map({'path': total_path, 'cost': result.cost + spur_cost}))
if len(B) == 0:
break
B.sort(key=lambda p: (p.cost, len(p.path)))
best_b = B.pop(0)
if best_b.cost != float('inf'):
A.append(best_b.path)
return A
paths = YenKSP(graph, 'A', 'F', 4)
print(paths)
Produces:
[['A', 'B', 'D', 'F'], ['A', 'E', 'D', 'F'], ['A', 'B', 'C', 'G', 'F'], ['A', 'B', 'D', 'C', 'G', 'F']]
The main issue was that when there was no path found, your default returned a path with 0 cost. So when sorted by path cost, these paths were appearing as the best choice in B and being added to A. I changed the default path cost to float('inf'). Doing so revealed an error that could occur when you tried to remove the same edge twice from graph_clone (inside for path in A: ...), so I added an if check to conditionally remove the edge. The two last things the diff indicate that I did were (a) imitate your dotmap.Map class (you can remove this and uncomment the import), and (b) only add a path to the resultset A if the cost is finite.

Using tuple as key for nested properties with json.dump

Not sure how to use a tuple as a set of strings the way I would like.
I would like my json to look like:
'item': {
'a': {
'b': {
'c': 'somevalue'
}
}
}
Which could be done with:
item = {}
item['a']['b']['c'] = "somevalue"
However a, b, and c are dynamic, so I understand I need to use a tuple, but this does not do what I would like:
item = {}
path = ('a','b','c')
item[path] = "somevalue"
json.dump(item, sys.stdout)
So I am getting the error:
TypeError("key " + repr(key) + " is not a string"
How do I dynamically get item['a']['b']['c']?
AFAIK there are no builtins for this task, so you need to write a couple of recursive functions:
def xset(dct, path, val):
if len(path) == 1:
dct[path[0]] = val
else:
if path[0] not in dct: dct[path[0]] = {}
xset(dct[path[0]], path[1:], val)
def xget(dct, path):
if len(path) == 1:
return dct[path[0]]
else:
return xget(dct[path[0]], path[1:])
Usage:
>>> d = {}
>>> xset(d, ('a', 'b', 'c'), 6)
>>> d
{'a': {'b': {'c': 6}}}
>>> xset(d, ('a', 'b', 'd', 'e'), 12)
>>> d
{'a': {'b': {'c': 6, 'd': {'e': 12}}}}
>>> xget(d, ('a', 'b', 'c'))
6
Try this:
item = {}
for i in reversed(path):
tmp = {**item}
item ={}
item[i] = {**tmp} if path.index(i)!=len(path)-1 else 'somevalue'

urlencode a multidimensional dictionary in python

How can I get a URL-encoded version of a multidimensional dictionary in Python? Unfortunately, urllib.urlencode() only works in a single dimension. I would need a version capable of recursively encoding the dictionary.
For example, if I have the following dictionary:
{'a': 'b', 'c': {'d': 'e'}}
I want to obtain the following string:
a=b&c[d]=e
OK people. I implemented it myself:
import urllib
def recursive_urlencode(d):
"""URL-encode a multidimensional dictionary.
>>> data = {'a': 'b&c', 'd': {'e': {'f&g': 'h*i'}}, 'j': 'k'}
>>> recursive_urlencode(data)
u'a=b%26c&j=k&d[e][f%26g]=h%2Ai'
"""
def recursion(d, base=[]):
pairs = []
for key, value in d.items():
new_base = base + [key]
if hasattr(value, 'values'):
pairs += recursion(value, new_base)
else:
new_pair = None
if len(new_base) > 1:
first = urllib.quote(new_base.pop(0))
rest = map(lambda x: urllib.quote(x), new_base)
new_pair = "%s[%s]=%s" % (first, ']['.join(rest), urllib.quote(unicode(value)))
else:
new_pair = "%s=%s" % (urllib.quote(unicode(key)), urllib.quote(unicode(value)))
pairs.append(new_pair)
return pairs
return '&'.join(recursion(d))
if __name__ == "__main__":
import doctest
doctest.testmod()
Still, I'd be interested to know if there's a better way to do this. I can't believe Python's standard library doesn't implement this.
Something like this?
a = {'a': 'b', 'c': {'d': 'e'}}
url = urllib.urlencode([('%s[%s]'%(k,v.keys()[0]), v.values()[0] ) if type(v)==dict else (k,v) for k,v in a.iteritems()])
url = 'a=b&c%5Bd%5D=e'
Based on the code of #malaney, I think that the code below emulates the PHP function http_build_query() quite well.
#!/usr/bin/env python3
import urllib.parse
def http_build_query(data):
parents = list()
pairs = dict()
def renderKey(parents):
depth, outStr = 0, ''
for x in parents:
s = "[%s]" if depth > 0 or isinstance(x, int) else "%s"
outStr += s % str(x)
depth += 1
return outStr
def r_urlencode(data):
if isinstance(data, list) or isinstance(data, tuple):
for i in range(len(data)):
parents.append(i)
r_urlencode(data[i])
parents.pop()
elif isinstance(data, dict):
for key, value in data.items():
parents.append(key)
r_urlencode(value)
parents.pop()
else:
pairs[renderKey(parents)] = str(data)
return pairs
return urllib.parse.urlencode(r_urlencode(data))
if __name__ == '__main__':
payload = {
'action': 'add',
'controller': 'invoice',
'code': 'debtor',
'InvoiceLines': [
{'PriceExcl': 150, 'Description': 'Setupfee'},
{'PriceExcl':49.99, 'Description':'Subscription'}
],
'date': '2016-08-01',
'key': 'Yikes&ampersand'
}
print(http_build_query(payload))
payload2 = [
'item1',
'item2'
]
print(http_build_query(payload2))
I think the code below may be what you want
import urllib.parse
def url_encoder(params):
g_encode_params = {}
def _encode_params(params, p_key=None):
encode_params = {}
if isinstance(params, dict):
for key in params:
encode_key = '{}[{}]'.format(p_key,key)
encode_params[encode_key] = params[key]
elif isinstance(params, (list, tuple)):
for offset,value in enumerate(params):
encode_key = '{}[{}]'.format(p_key, offset)
encode_params[encode_key] = value
else:
g_encode_params[p_key] = params
for key in encode_params:
value = encode_params[key]
_encode_params(value, key)
if isinstance(params, dict):
for key in params:
_encode_params(params[key], key)
return urllib.parse.urlencode(g_encode_params)
if __name__ == '__main__':
params = {'name': 'interface_name', 'interfaces': [{'interface': 'inter1'}, {'interface': 'inter2'}]}
print(url_encoder(params))
the output is
interfaces%5B1%5D%5Binterface%5D=inter2&name=interface_name&interfaces%5B0%5D%5Binterface%5D=inter1
which is look like
interfaces[1][interface]=inter2&name=interface_name&interfaces[0][interface]=inter1
PS: you may want use OrderDict to replace dict above
The above solution only works for arrays with depth < 2. The code below will properly urlencode a multidimensional array of any depth.
#!/usr/bin/env python
import sys
import urllib
def recursive_urlencode(data):
def r_urlencode(data, parent=None, pairs=None):
if pairs is None:
pairs = {}
if parent is None:
parents = []
else:
parents = parent
for key, value in data.items():
if hasattr(value, 'values'):
parents.append(key)
r_urlencode(value, parents, pairs)
parents.pop()
else:
pairs[renderKey(parents + [key])] = renderVal(value)
return pairs
return urllib.urlencode(r_urlencode(data))
def renderKey(parents):
depth, outStr = 0, ''
for x in parents:
str = "[%s]" if depth > 0 else "%s"
outStr += str % renderVal(x)
depth += 1
return outStr
def renderVal(val):
return urllib.quote(unicode(val))
def main():
print recursive_urlencode(payload)
if __name__ == '__main__':
sys.exit(main())
The function get_encoded_url_params() takes a dict as argument and returns url encoded form of the dict.
def get_encoded_url_params(d):
"""URL-encode a nested dictionary.
:param d = dict
:returns url encoded string with dict key-value pairs as query parameters
e.g.
if d = { "addr":{ "country": "US", "line": ["a","b"] },
"routing_number": "011100915", "token": "asdf"
}
:returns 'addr[country]=US&addr[line][0]=a&addr[line][1]=b&routing_number=011100915&token=asdf'
or 'addr%5Bcountry%5D=US&addr%5Bline%5D%5B0%5D=a&addr%5Bline%5D%5B1%5D=b&routing_number=011100915&token=asdf'
(which is url encoded form of the former using quote_plus())
"""
def get_pairs(value, base):
if isinstance(value, dict):
return get_dict_pairs(value, base)
elif isinstance(value, list):
return get_list_pairs(value, base)
else:
return [base + '=' + str(value)]
# use quote_plus() to get url encoded string
# return [quote_plus(base) + '=' + quote_plus(str(value))]
def get_list_pairs(li, base):
pairs = []
for idx, value in enumerate(li):
new_base = base + '[' + str(idx) + ']'
pairs += get_pairs(value, new_base)
return pairs
def get_dict_pairs(d, base=''):
pairs = []
for key, value in d.items():
new_base = key if base == '' else base + '[' + key + ']'
pairs += get_pairs(value, new_base)
return pairs
return '&'.join(get_dict_pairs(d))
what about json.dumps and json.loads?
d = {'a': 'b', 'c': {'d': 'e'}}
s = json.dumps(d) # s: '{"a": "b", "c": {"d": "e"}}'
json.loads(s) # -> d
what about this simplified version:
def _clean(value):
return urllib.quote(unicode(value))
'&'.join([ v for val in [[ "%s[%s]=%s"%(k,ik, _(iv))
for ik, iv in v.items()] if type(v)==dict else ["%s=%s"%(k,_(v))]
for k,v in data.items() ]
for v in val ])
I agree is not readable, maybe flattening the list can be better done with itertools.chain instead of another list comprehension.
This only goes 1 level deeper, yours can go N levels deeper if you would add some logic to manage N numbers of "[%s]" depending on the level, but I guess is not that necesary
If you want to convert python dict/list/nested to PHP Array like urlencoded string.
In python, most of the data type you want to convert to urlencoded maybe: dict list tuple nested of them, Like
a = [1, 2]
print(recursive_urlencode(a))
# 0=1&1=2
a2 = (1, '2')
print(recursive_urlencode(a2))
# 0=1&1=2
b = {'a': 11, 'b': 'foo'}
print(recursive_urlencode(b))
# a=11&b=foo
c = {'a': 11, 'b': [1, 2]}
print(recursive_urlencode(c))
# a=11&b[0]=1&b[1]=2
d = [1, {'a': 11, 'b': 22}]
print(recursive_urlencode(d))
# 0=1&1[a]=11&1[b]=22
e = {'a': 11, 'b': [1, {'c': 123}, [3, 'foo']]}
print(recursive_urlencode(e))
# a=11&b[0]=1&b[1][c]=123&b[2][0]=3&b[2][1]=foo
https://github.com/Viky-zhang/to_php_post_arr
P.S. some code from: https://stackoverflow.com/a/4014164/2752670

Categories