Python dictionary changes when using intermediate variable - python

I'm feeding a function a string, which reads the string char by char. Based on the char being worked on, a JSON template is called from a dictionary, edited slightly and saved to a final dictionary, which will be parsed to JSON and saved.
The problem is that this template dictionary should stay constant, but it doesn't. Somehow, the values I write to the intermediate variable gets saved to the original template dictionary, messing up the subsequent data I'm trying to save.
Am I missing some basic concept of the dictionary? This is my first time working with dictionaries to such an extent so I wouldn't even be surprised.
The template dictionary:
self.map_legend = {"#": {"Id": 100, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Wall", "PlayerNumber": 0},
"-": {"Id": 200, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Shield", "PlayerNumber": 0},
"x": {"Id": 300, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Alien", "PlayerNumber": 0},
"|": {"Id": 400, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "AlienBullet", "PlayerNumber": 0},
"!": {"Id": 500, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Missile", "PlayerNumber": 0},
"i": {"Id": 500, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Missile", "PlayerNumber": 1},
"M": {"Id": 600, "Alive": False, "X": 0, "Y": 0, "Width": 3, "Height": 1, "Type": "MissileController", "PlayerNumber": 0},
"X": {"Id": 700, "Alive": False, "X": 0, "Y": 0, "Width": 3, "Height": 1, "Type": "AlienFactory", "PlayerNumber": 0},
"A": {"Id": 800, "Alive": False, "X": 0, "Y": 0, "Width": 3, "Height": 1, "Type": "Ship", "PlayerNumber": 0},
"V": {"Id": 800, "Alive": False, "X": 0, "Y": 0, "Width": 3, "Height": 1, "Type": "Ship", "PlayerNumber": 1},
" ": {"Id": 900, "Alive": False, "X": 0, "Y": 0, "Width": 1, "Height": 1, "Type": "Space", "PlayerNumber": 0}}
The problem code:
for char in self.initial_game_map:
if char != "\n":
element = self.map_legend[char]
self.id_counters[char] += 1
element["Id"] = self.id_counters[char] + element["Id"]
element["Alive"] = True
element["X"] = char_counter % self.state_json["Map"]["Height"]
element["Y"] = char_counter / self.state_json["Map"]["Height"]
print self.map_legend[char]
print element
row.append(element)
element = {}
char_counter += 1
else:
self.state_json["Map"]["Rows"].append(row)
row = []
Some output:
V
{'Width': 3, 'PlayerNumber': 1, 'Y': 1, 'X': 2, 'Type': 'Ship', 'Id': 801, 'Alive': True, 'Height': 1}
{'Width': 3, 'PlayerNumber': 1, 'Y': 1, 'X': 2, 'Type': 'Ship', 'Id': 801, 'Alive': True, 'Height': 1}
#
{'Width': 1, 'PlayerNumber': 0, 'Y': 0, 'X': 18, 'Type': 'Wall', 'Id': 103, 'Alive': True, 'Height': 1}
{'Width': 1, 'PlayerNumber': 0, 'Y': 0, 'X': 18, 'Type': 'Wall', 'Id': 103, 'Alive': True, 'Height': 1}
the element variable is behaving as its supposed to, but you can see that self.map_legend assumes the value of element for some reason after element is changed, which is NOT what I want. What's going on?

element and self.map_legend[char] point to the same dictionary and thus if you update element you will update the values of the dictionary self.map_legend[char]. You seem to want a copy so use:
element = self.map_legend[char].copy()
Reference in the python documentation: https://docs.python.org/2/library/copy.html. As the dictionaries are shallow you don't need .deepcopy()

You need to deepcopy the dictionary, else the original will get modified.
from copy import deepcopy
element = deepcopy(self.map_legend[char])

Related

Converting JSON coordinates to numpy array

I would like to convert a JSON file back into a png image or a NumPy array.
The file JSON file consists of a list of coordinates and other metadata. As an example. it would look like this :
"firstEditDate": "2019-12-02T19:05:45.393Z",
"lastEditDate": "2020-06-30T13:21:33.371Z",
"folder": "/Pictures/poly",
"objects": [
{
"classIndex": 5,
"layer": 0,
"polygon": [
{
"x": 0,
"y": 0
},
{
"x": 1699.7291626931146,
"y": 0
},
{
"x": 1699.7291626931146,
"y": 1066.87392714095
},
{
"x": 0,
"y": 1066.87392714095
}
],
},
{
"classIndex": 2,
"layer": 0,
"polygon": [
{
"x": 844.2300556586271,
"y": 711.8243676199173
},
{
"x": 851.156462585034,
"y": 740.5194820293175
},
{
"x": 854.1249226963513,
"y": 744.477428844407
},
{
"x": 854.1249226963513,
"y": 747.4458889557243
},
(coordinates should be rounded to the nearest ones prior to creating the array or image)
The dimension of the array/ picture should be 1727 x 971
Is there any function in python that can convert the file either into an array with the value inside the array of the ClassIndex? Or into a picture where each ClassIndex is assigned to a specific color?
Here is a solution:
import matplotlib.pyplot as plt
import numpy as np
import mahotas.polygon as mp
json_dict = {
"firstEditDate": "2019-12-02T19:05:45.393Z",
"lastEditDate": "2020-06-30T13:21:33.371Z",
"folder": "/Pictures/poly",
"objects": [{
"classIndex": 1,
"layer": 0,
"polygon": [
{"x": 170, "y": 674},
{"x": 70, "y": 674},
{"x": 70, "y": 1120},
{"x": 870, "y": 1120},
{"x": 870, "y": 674},
{"x": 770, "y": 674},
{"x": 770, "y": 1020},
{"x": 170, "y": 1020},
],
}, {
"classIndex": 2,
"layer": 0,
"polygon": [
{"x": 220, "y": 870},
{"x": 220, "y": 970},
{"x": 720, "y": 970},
{"x": 720, "y": 870},
]
}, {
"classIndex": 3,
"layer": 0,
"polygon": [
{"x": 250, "y": 615},
{"x": 225, "y": 710},
{"x": 705, "y": 840},
{"x": 730, "y": 745},
]
}, {
"classIndex": 4,
"layer": 0,
"polygon": [
{"x": 350, "y": 380},
{"x": 300, "y": 465},
{"x": 730, "y": 710},
{"x": 780, "y": 630},
]
}, {
"classIndex": 5,
"layer": 0,
"polygon": [
{"x": 505, "y": 180},
{"x": 435, "y": 250},
{"x": 790, "y": 605},
{"x": 855, "y": 535},
]
}, {
"classIndex": 6,
"layer": 0,
"polygon": [
{"x": 700, "y": 30},
{"x": 615, "y": 80},
{"x": 870, "y": 515},
{"x": 950, "y": 465},
]
}]
}
canvas = np.zeros((1000,1150))
for obj in json_dict["objects"]:
pts = [(round(p["x"]),round(p["y"])) for p in obj["polygon"]]
mp.fill_polygon(pts, canvas, obj["classIndex"])
plt.imshow(canvas.transpose())
plt.colorbar()
plt.show()
Output:

How to remove doubles by nested attributes in Python?

I've got a list of records in which the details contain some doubles. In the list of dicts below you see that the first 3 records (with id 1, 2 and 3) have the same "count" for all the details with a dir "s" (even though their respective detail id's differ). I would like to remove all records from the root list, for which all the counts of the details with a dir "s" are the same as the counts of the details with a dir "s" in a previous record. So from the list below I would want the records with ids 2 and 3 to be removed from the records list.
I've been writing nested loops for a while, but I can't really find a way of doing this. Plus, my code constantly becomes this complete mess real quick.
What would be a logical and Pythonic way of doing this?
records = [
{
'id': 1,
'details': [
{"id": 10, "dir": "s", "count": "1"},
{"id": 20, "dir": "u", "count": "6"},
{"id": 30, "dir": "s", "count": "1"}
]
},
{
'id': 2,
'details': [
{"id": 40, "dir": "s", "count": "1"},
{"id": 50, "dir": "u", "count": "7"},
{"id": 60, "dir": "s", "count": "1"}
]
},
{
'id': 3,
'details': [
{"id": 70, "dir": "s", "count": "1"},
{"id": 80, "dir": "u", "count": "8"},
{"id": 90, "dir": "s", "count": "1"}
]
},
{
'id': 4,
'details': [
{"id": 100, "dir": "s", "count": "999"},
{"id": 110, "dir": "up", "count": "6"},
{"id": 120, "dir": "s", "count": "999"}
]
},
]
Use a set and the key based on the two elements of the dict that you consider the definition of a 'duplicate'.
Simple example to uniquify:
seen=set()
for di in records:
for sdi in di['details']:
key=(sdi['dir'], sdi['count'])
if key not in seen:
seen.add(key)
print(sdi)
else:
# deal with the duplicate?
pass
Prints:
{'id': 10, 'dir': 's', 'count': '1'}
{'id': 20, 'dir': 'u', 'count': '6'}
{'id': 50, 'dir': 'u', 'count': '7'}
{'id': 80, 'dir': 'u', 'count': '8'}
{'id': 100, 'dir': 's', 'count': '999'}
{'id': 110, 'dir': 'up', 'count': '6'}
Giving a first pass the what I think you mean:
seen=set()
new_rec=[]
for di in records:
new_di={}
new_di['id']=di['id']
new_li=[]
for sdi in di['details']:
key=(sdi['dir'], sdi['count'])
if key not in seen:
seen.add(key)
new_li.append(sdi)
else:
# deal with the duplicate?
pass
new_di['details']=new_li
new_rec.append(new_di)
Which results in:
[ { 'id': 1,
'details': [ { 'id': 10,
'dir': 's',
'count': '1'},
{ 'id': 20,
'dir': 'u',
'count': '6'}]},
{ 'id': 2,
'details': [ { 'id': 50,
'dir': 'u',
'count': '7'}]},
{ 'id': 3,
'details': [ { 'id': 80,
'dir': 'u',
'count': '8'}]},
{ 'id': 4,
'details': [ { 'id': 100,
'dir': 's',
'count': '999'},
{ 'id': 110,
'dir': 'up',
'count': '6'}]}]

Python/PySpark parse JSON string with numbered attributes

I need to store JSON strings like the one below in some file format different from plaintext (e.g: parquet):
{
"vidName": "Foo",
"vidInfo.0.size.length": 10,
"vidInfo.0.size.width": 10,
"vidInfo.0.quality": "Good",
"vidInfo.1.size.length": 7,
"vidInfo.1.size.width": 3,
"vidInfo.1.quality": "Bad",
"vidInfo.2.size.length": 10,
"vidInfo.2.size.width": 2,
"vidInfo.2.quality": "Excelent"
}
There's no known bound for the index of vidInfo (can be 10, 20). Thus I want either to have vidInfos in an array, or explode such JSON object into multiple smaller objects.
I found this question: PHP JSON parsing (number attributes?)
But it is in PHP which I do not quite understand. And I am not sure whether it is same as what I need.
The intermediate data should be something like this:
{
"vidName": "Foo",
"vidInfo": [
{
"id": 0,
"size": {
"length": 10,
"width": 10
},
"quality": "Good"
},
{
"id": 1,
"size": {
"length": 7,
"width": 3
},
"quality": "Bad"
},
{
"id": 2,
"size": {
"length": 10,
"width": 2
},
"quality": "Excelent"
}
]
}
or like this:
{
"vidName": "Foo",
"vidInfo": [
{
"size": {
"length": 10,
"width": 10
},
"quality": "Good"
},
{
"size": {
"length": 7,
"width": 3
},
"quality": "Bad"
},
{
"size": {
"length": 10,
"width": 2
},
"quality": "Excelent"
}
]
}
I am stuck, and would need some hints to move on.
Could you please help?
Thanks a lot for your help.
I found this library https://github.com/amirziai/flatten which does the trick.
In [154]: some_json = {
...: "vidName": "Foo",
...: "vidInfo.0.size.length": 10,
...: "vidInfo.0.size.width": 10,
...: "vidInfo.0.quality": "Good",
...: "vidInfo.1.size.length": 7,
...: "vidInfo.1.size.width": 3,
...: "vidInfo.1.quality": "Bad",
...: "vidInfo.2.size.length": 10,
...: "vidInfo.2.size.width": 2,
...: "vidInfo.2.quality": "Excelent"
...: }
In [155]: some_json
Out[155]:
{'vidName': 'Foo',
'vidInfo.0.size.length': 10,
'vidInfo.0.size.width': 10,
'vidInfo.0.quality': 'Good',
'vidInfo.1.size.length': 7,
'vidInfo.1.size.width': 3,
'vidInfo.1.quality': 'Bad',
'vidInfo.2.size.length': 10,
'vidInfo.2.size.width': 2,
'vidInfo.2.quality': 'Excelent'}
In [156]: from flatten_json import unflatten_list
...: import json
...: nested_json = unflatten_list(json.loads(json.dumps(some_json)), '.')
In [157]: nested_json
Out[157]:
{'vidInfo': [{'quality': 'Good', 'size': {'length': 10, 'width': 10}},
{'quality': 'Bad', 'size': {'length': 7, 'width': 3}},
{'quality': 'Excelent', 'size': {'length': 10, 'width': 2}}],
'vidName': 'Foo'}

New to Python and working on it to mess with JSON files

So I'm new to phython, and I was wondering on how I could modify(adding or subtracting) each individual "x" in one go.
"Position": {
"7,-5": {
"id": "58",
"y": -5,
"x": 7
},
"2,-4": {
"id": "183",
"y": -4,
"x": 2
},
"-4,-1": {
"id": "190",
"y": -1,
"x": -4
}
I tried doing
import json
with open ('position.txt', 'r+') as f:
position_data = json.load(f)
position_data['Position']['x'] = +1
TypeError: list indices must be integers or slices, not str
This is what I want
"Position": {
"7,-5": {
"id": "58",
"y": -5,
"x": 8
},
"2,-4": {
"id": "183",
"y": -4,
"x": 3
},
"-4,-1": {
"id": "190",
"y": -1,
"x": -3
}
I'm not sure on how to go from here. Please advice.
you could do something like this:
for key in position_data['Position'].keys():
position_data['Position'][key]['x'] += 1
for value in position_data['Position'].values():
value['x'] += 1
Use itervalues in Python 2 for better efficiency.
Demo (since I got downvoted without explanation):
from pprint import pprint
position_data = {
"Position": {
"7,-5": {
"id": "58",
"y": -5,
"x": 7
},
"2,-4": {
"id": "183",
"y": -4,
"x": 2
},
"-4,-1": {
"id": "190",
"y": -1,
"x": -4
}
}
}
pprint(position_data)
for value in position_data['Position'].values():
value['x'] += 1
pprint(position_data)
Output:
{'Position': {'-4,-1': {'id': '190', 'x': -4, 'y': -1},
'2,-4': {'id': '183', 'x': 2, 'y': -4},
'7,-5': {'id': '58', 'x': 7, 'y': -5}}}
{'Position': {'-4,-1': {'id': '190', 'x': -3, 'y': -1},
'2,-4': {'id': '183', 'x': 3, 'y': -4},
'7,-5': {'id': '58', 'x': 8, 'y': -5}}}

JSON to Python not working correcly

I have the following json string
{
"meta": {
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total_count": 3
},
"objects": [
{
"id": 1,
"name": "Exercicios_EO_JorgeLoureiro-cópia-não.está.exactament.igual.pdf\n",
"resource_uri": "/api/open/1/"
},
{
"id": 2,
"name": "api.py\n",
"resource_uri": "/api/open/2/"
},
{
"id": 3,
"name": "models.py\n",
"resource_uri": "/api/open/3/"
}
]
}
which i get from doing a server call and reading the response
response = url2.urlopen("http://127.0.0.1:8000/api/open/?format=json")
data = response.read()
However, when i load it from json to python via
res = json.loads(data)
i get, from printing res:
{u'meta': {u'previous': None, u'total_count': 3, u'offset': 0, u'limit': 20, u'next': None}, u'objects': [{u'resource_uri': u'/api/open/1/', u'id': 1, u'name': u'Exercicios_EO_JorgeLoureiro-c\xf3pia-n\xe3o.est\xe1.exactament.igual.pdf\n'}, {u'resource_uri': u'/api/open/2/', u'id': 2, u'name': u'api.py\n'}, {u'resource_uri': u'/api/open/3/', u'id': 3, u'name': u'models.py\n'}]}
{"meta": {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 3}, "objects": [{"id": 1, "name": "Exercicios_EO_JorgeLoureiro-cópia-não.está.exactament.igual.pdf\n", "resource_uri": "/api/open/1/"}, {"id": 2, "name": "api.py\n", "resource_uri": "/api/open/2/"}, {"id": 3, "name": "models.py\n", "resource_uri": "/api/open/3/"}]}
{u'meta': {u'previous': None, u'total_count': 3, u'offset': 0, u'limit': 20, u'next': None}, u'objects': [{u'resource_uri': u'/api/open/1/', u'id': 1, u'name': u'Exercicios_EO_JorgeLoureiro-c\xf3pia-n\xe3o.est\xe1.exactament.igual.pdf\n'}, {u'resource_uri': u'/api/open/2/', u'id': 2, u'name': u'api.py\n'}, {u'resource_uri': u'/api/open/3/', u'id': 3, u'name': u'models.py\n'}]}
{"meta": {"limit": 20, "next": null, "offset": 0, "previous": null, "total_count": 3}, "objects": [{"id": 1, "name": "Exercicios_EO_JorgeLoureiro-cópia-não.está.exactament.igual.pdf\n", "resource_uri": "/api/open/1/"}, {"id": 2, "name": "api.py\n", "resource_uri": "/api/open/2/"}, {"id": 3, "name": "models.py\n", "resource_uri": "/api/open/3/"}]}
{u'meta': {u'previous': None, u'total_count': 3, u'offset': 0, u'limit': 20, u'next': None}, u'objects': [{u'resource_uri': u'/api/open/1/', u'id': 1, u'name': u'Exercicios_EO_JorgeLoureiro-c\xf3pia-n\xe3o.est\xe1.exactament.igual.pdf\n'}, {u'resource_uri': u'/api/open/2/', u'id': 2, u'name': u'api.py\n'}, {u'resource_uri': u'/api/open/3/', u'id': 3, u'name': u'models.py\n'}]}
which is like 3 copies of what I really wanted, which is just that json string to python.
Any clue of what might be going on?
Thanks in advance
Here is the full code
response = url2.urlopen("http://127.0.0.1:8000/api/open/?format=json")
data = response.read()
print data
print "\n"
res = json.loads(data)
print res

Categories

Resources