Using $each with $push on all fields in python - python

I have a python dictionary representing updates to be made to a mongo document:
{
'1': [{'x': 21, 'y': 37}, {'x': 12, 'y': 41}],
'2': [{'x': 17, 'y': 19}, {'x': 82, 'y': 31}],
...
...
'r2': [{'x': 21, 'y': 37}, {'x': 12, 'y': 41}]
...
...
}
Fields can be many, I have shown just '1', '2' and 'r1' for simplicity.
I want to push all the values in the various arrays to their respective fields in a document already in mongo (using a field 'dated', which I have in a variable).
For ex: If the document in the db was:
{
'1': [{'x': 47, 'y': 33}]
...
}
After update, it should become:
{
'1': [{'x':47, 'y': 33}, {'x': 21, 'y': 37}, {'x': 12, 'y': 41}]
...
}
I do not want to use $pushAll because the mongodb documentation says that its deprecated. So how do I use $push with $each (or some other operator) on every dictionary key whose value is an array (in python 2.7 using pymongo)?
EDIT:
I am finding the document using a field 'dated' which is of the form 'dd-mm-yyyy'.
If in a document a field does not exists it should be created

You can use collection.find_one_and_update in Pymongo 3.x or collection.find_and_modify(DEPRECATED).
d = {
'1': [{'x': 21, 'y': 37}, {'x': 12, 'y': 41}],
'2': [{'x': 17, 'y': 19}, {'x': 82, 'y': 31}]}
for k, v in d.items():
col.find_and_modify({k: {"$exists": True}}, {"$push": {k: {"$each": v}}})
Pymongo 3.x
for k, v in d.items():
col.find_one_and_update({k: {"$exists": True}}, {"$push": {k: {"$each": v}}})
Edit:
Use the update_many with upsert=True or update with upsert = True and multi = True perform an insert if no documents match your filter. However you will need to iterate through each key to update existing document
if col.count() > 0:
col.insert(d)
else:
# your_update

Related

How to iterate dynamically over a dictionary

How to iterate over a dictionary / JSON using a dynamic query.
For example consider the below dict
dict = {'Adam': {
'English': {
'Score': 99,
'Time': 3400,
'Classes': 4},
'Math': {
'Score': 45,
'Time': 779,
'Classes': 5}},
'Tim': {
'English': {
'Score': 74,
'Time': 12,
'Classes': 99},
'Math': {
'Score': 12,
'Time': 333,
'Classes': 1}}
}
I want to set the value of a given path for example
path = '/Adam/English/Score'
new_value = 87
Note that the value assigned could be another dict as well for example
path = '/Adam/English'
new_value = {'Score': 11,
'Time': 2,
'Classes': 9}
Any help would be useful.
Edit: Below is my attempt
keys = path.split('/')[1:]
new_data = None
for key in keys:
if new_data is None:
new_data = dict[key]
else:
new_data = new_data[key]
new_data = new_value
print(dict)
But here the dict still has the old value
I made some assumptions, for example that '/' is not part of any dict-keys and that the path must be valid. Adjust the function as needed.
def deep_set(d, path, value):
sep = '/'
*trail, last = path.strip(sep).split(sep)
for part in trail:
d = d[part]
d[last] = value
Demo:
>>> d = {'a': 1}
>>> deep_set(d, 'a', 2)
>>> d
{'a': 2}
>>> d = {'a': {'b': 1}}
>>> deep_set(d, 'a/b', 2)
>>> d
{'a': {'b': 2}}
edit:
Note that if there are consecutive '/' characters then the empty string will be looked up as a dict key. e.g.
'a/b//c'.split('/') -> ['a', 'b', '', 'c']
It's unclear whether you want to treat leading/trailling '/' characters as part of the path or not (in my function, they are removed with str.strip). Again, adjust as needed.

Serialize a custom object inside json object

I am using a third party python library, which returns some data in lists and dictionaries inside them (JSON format). For example, here's a sample entry:
data = [{'a': 1, 'b': 80, 'c': 42, 'd': [{'r': 0, 's': '1', 'u': 5}], 'id': 10, 'k': 60, 'v': 0, 'm':
{'ty': 'djp', 'nr': '10', 'bc': Adder('179'), 'in': 3}}, {'a': 1, 'b': 70, 'c': 42, 'd': [{'r': 0, 's':
'1', 'u': 5}], 'y': 10, 'k': 60, 'v': 0, 'm': {'ty': 'djp', 'dx': '10', 'tx': Adder('179'), 'in': 3}}]
My problem is with 'Adder' class which is an object. Everything else are just strings.
So, when I do:
json.dumps(data)
It causes an error message:
Adder('179') is not JSON serializable.
I don't care about serializing this Adder class, so I would somehow like to just make it a string e.g. "Adder(179)" if possible. Is it possible to make the dumps function work? Class Adder is not my own, maybe I can make it serialized or tell it what to do with it by editing the source or something? Any simple way would be fine.
Just specify a custom encoder class:
class RobustEncoder(json.JSONEncoder):
def default(self, o):
try:
return super(RobustEncoder, self).default(o)
except TypeError:
return str(o)
json.dumps(data, cls=RobustEncoder)
The keyword argument cls is documented in the docs of json.dump.

Convert dictionary values from list to dictionary

I have a function which results
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
I have another function i need to pass 2132,L,Y values from dict1 as an arguments and it should result 452.2
def getx(a, b, c):
try:
return dict1[a][b][c]
except:
return None
when i give dict1['2132'] which results [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}]
i want dict1['2132']['L']['Y'] should result as 452.2
so i need my dictionary
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
to show as
dict1 = {'2132': {{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}}, '2345': {{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}}
OR is there any other way that we can pull the 4th value when the dict1 is
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
here is your solution
#v1='2132' v2='L' v3='Y'
def Solution(v1,v2,v3):
if v1 in dict1.keys():
for i in dict1[v1]:
if v2 in i.keys():
if v3 in i[v2]:
return i[v2][v3]
return None
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'C': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}},{'6': {'Y': '45.23'}}]}
print(Solution('2132','L','Y'))
How about this:
from collections import defaultdict
for key,values in dict1.items():
temp_dict = defaultdict(dict)
for val in values: #values is a list of dict
for k,v in val.items():
temp_dict[k].update(v)
dict1[key] = dict(temp_dict)
print(dict1)
#{'2132': {'L': {'Y': '452.2', 'N': '21'}}, '2345': {'L': {'Y': '87'}, 'C': {'N': '56'}, '6': {'Y': '45.23'}}}
And then
def getx(a, b, c):
try:
return dict1[a][b][c]
except:
return None
print(getx('2132','L','Y'))
#452.2

Cassandra ResultSet gets empty after traversing it once

I have a python script that pulls data out of a database. The problem is it is only pulling one item type and not the complete dataset into the JSON serialized object.
The object I'm trying to get comes from this:
STATS = ['min', 'max', 'mean','percentile1', 'percentile5', 'median', 'percentile95', 'percentile99', 'total']
The only problem is for some reason it will only ever take the first one. It that example it was 'min' if I switch the first to be 'percentile' such as this:
STATS = ['percentile1','min', 'max', 'mean',, 'percentile5', 'median', 'percentile95', 'percentile99', 'total']
Then this will load only the 'percentile' data. It is not including any of the rest. It is querying the correct data with each one but will only pass the first one to the Rickshaw.js to draw the graphs.
I am serializing the data with this:
def get_series(self, stationid, metric, monthly=True):
'''
Format results into json-ready results for Rickshaw.js.
'''
allResults = {}
if monthly:
rs = self.get_monthly_report(stationid, metric)
else:
rs = self.get_daily_report(stationid, metric)
for field in STATS:
series = self.format_series(rs, field)
allResults[field] = series
return json.dumps(allResults, default=json_serial)
def format_series(self, records, field):
'''
JSON formatting helper.
'''
data = []
for record in records:
data.append({'x' : time.mktime(record['date'].timetuple()), 'y' : record[field]})
return data
If you need more of the code. I can gladly provide. Thank you!
I inserted some print commands
def get_series(self, stationid, metric, monthly=True):
'''
Format results into json-ready results for Rickshaw.js.
'''
allResults = {}
if monthly:
rs = self.get_monthly_report(stationid, metric)
else:
rs = self.get_daily_report(stationid, metric)
for field in STATS:
print "The field is"
print (field)
series = self.format_series(rs, field)
print "The Series is"
print (series)
allResults[field] = series
return json.dumps(allResults, default=json_serial)
this is what appears:
The field is
min
The Series is
[{'y': 0, 'x': 1388552400.0}, {'y': 0, 'x': 1391230800.0}, {'y': 0, 'x': 1393650000.0}, {'y': 19, 'x': 1396324800.0}, {'y': 52, 'x': 1398916800.0}, {'y': 13, 'x': 1401595200.0}, {'y': 37, 'x': 1404187200.0}, {'y': 10, 'x': 1406865600.0}, {'y': 4, 'x': 1409544000.0}, {'y': 49, 'x': 1412136000.0}, {'y': 28, 'x': 1414814400.0}, {'y': 0, 'x': 1417410000.0}, {'y': 0, 'x': 1420088400.0}, {'y': 46, 'x': 1422766800.0}, {'y': 60, 'x': 1425186000.0}, {'y': 52, 'x': 1427860800.0}, {'y': 58, 'x': 1430452800.0}, {'y': 69, 'x': 1433131200.0}, {'y': 48, 'x': 1435723200.0}, {'y': 20, 'x': 1438401600.0}, {'y': 22, 'x': 1441080000.0}, {'y': 0, 'x': 1443672000.0}, {'y': 0, 'x': 1446350400.0}, {'y': 0, 'x': 1448946000.0}, {'y': 0, 'x': 1451624400.0}, {'y': 10, 'x': 1454302800.0}, {'y': 48, 'x': 1456808400.0}, {'y': 66, 'x': 1459483200.0}, {'y': 60, 'x': 1462075200.0}, {'y': 58, 'x': 1464753600.0}, {'y': 0, 'x': 1467345600.0}, {'y': 17, 'x': 1470024000.0}, {'y': 27, 'x': 1472702400.0}, {'y': 31, 'x': 1475294400.0}, {'y': 0, 'x': 1477972800.0}, {'y': 10, 'x': 1480568400.0}, {'y': 65, 'x': 1483246800.0}]
The field is
max
The Series is
[]
The field is
mean
The Series is
[]
The field is
percentile1
The Series is
[]
The field is
percentile5
The Series is
[]
The field is
median
The Series is
[]
The field is
percentile95
The Series is
[]
The field is
percentile99
The Series is
[]
The field is
total
The Series is
[]
The return value of get_month_report is of type
<cassandra.cluster.ResultSet object at 0x7fe1a6b6e910>
so when you traverse it once it depletes. You need to turn it to a list before traversing it multiple times, by the "list" operator:
if monthly:
rs = list(self.get_monthly_report(stationid, metric))
else:
rs = list(self.get_daily_report(stationid, metric))

Python Function: Given Scrabble Tile, What is the value?

I'm trying to write a simple function that given a letter, you are returned the value of the scrabble tile. Here is what I have:
def letterPoint(letter):
letter = letter.upper()
lettersWorthOne =(['A','E','I','N','O','R','S','T'])
lettersWorthTwo = (['D','G'])
lettersWorthThree = (['B','C','M','P'])
lettersWorthFour = (['F','H','U','V','W','Y'])
lettersWorthFive = (['K'])
lettersWorthEight = (['J','X'])
lettersWorthTen = (['Q','Z'])
if letterWorthOne:
print '1'
if letterWorthTwo:
print '2'
if letterWorthThree:
print '3'
if letterWorthFour:
print '4'
if letterWorthFive:
print '5'
if letterWorthEight:
print '8'
if letterWorthTen:
print '10'
Use a dictionary. Rather than
lettersWorthTwo = (['D','G']), etc.
You would have a data structure along the lines of:
letterValues = {'D':2, 'G':2, ... }
Then a lookup for value is simply:
letterValues['D'] # returns 2 for the value of the tile
To point out why your code doesn't work, because you're not comparing your letter to the list.
#Change from this:
if letterWorthOne:
print '1'
#to this, should work
if letter in letterWorthOne:
print '1'
.....
Use a python dictionary is the way to go.
Further to the solution someone has already posted. You can also construct a more content dictionary like this:
Letters = {
'a': { 'quantity' : 9, 'value': 1},
'b': { 'quantity' : 2, 'value': 3},
'c': { 'quantity' : 2, 'value': 3},
'd': { 'quantity' : 4, 'value': 2},
'e': { 'quantity' : 12, 'value': 1},
'f': { 'quantity' : 2, 'value': 4},
'g': { 'quantity' : 3, 'value': 2},
'h': { 'quantity' : 2, 'value': 4},
'i': { 'quantity' : 9, 'value': 1},
'j': { 'quantity' : 1, 'value': 8},
'k': { 'quantity' : 1, 'value': 5},
'l': { 'quantity' : 4, 'value': 1},
'm': { 'quantity' : 2, 'value': 3},
'n': { 'quantity' : 6, 'value': 1},
'o': { 'quantity' : 8, 'value': 1},
'p': { 'quantity' : 2, 'value': 3},
'q': { 'quantity' : 1, 'value': 10},
'r': { 'quantity' : 6, 'value': 1},
's': { 'quantity' : 4, 'value': 1},
't': { 'quantity' : 6, 'value': 1},
'u': { 'quantity' : 4, 'value': 1},
'v': { 'quantity' : 2, 'value': 4},
'w': { 'quantity' : 2, 'value': 4},
'x': { 'quantity' : 1, 'value': 8},
'y': { 'quantity' : 2, 'value': 4},
'z': { 'quantity' : 1, 'value': 10},
'*': { 'quantity' : 2, 'value': 0}
}
# to get to it's "content", like this:
Letters['a']
{'quantity': 9, 'value': 1}
# you can then get its 'value' or 'quantity' in a tile bag
Letters['a']['value']
1
# if you MUST use a function, do this with above dictionary, although it's quite pointless
def letter_point(letter):
return Letters[letter.upper()]['value']
In letterPoint(), letterWorthOne and lettersWorthOne are separate variables. Each lettersWorth* variable holds a list, and you appear to want letterWorthOne to contain a boolean value (True or False) specifying whether or not letter is in the lettersWorthOne list. To determine whether a value is in a collection, use operator in.
def letterPoint(letter):
letter = letter.upper()
lettersWorthOne =(['A','E','I','N','O','R','S','T'])
lettersWorthTwo = (['D','G'])
letterWorthOne = letter in lettersWorthOne
if letterWorthOne:
print '1'
letterWorthTwo = letter in lettersWorthTwo
if letterWorthTwo:
print '2'
# rest of values omitted for brevity
print 'E worth'
letterPoint('E')
print 'D worth'
letterPoint('D')
This program produces the following output:
E worth
1
D worth
2
This explains why your existing function doesn't work. But in the long run, I'd recommend using a dictionary to hold the value and quantity of each letter, and store the values as numbers instead of strings so that you can add the values of all letters in a word.
To use the code you began with, you could consider changing the lines to:
if letter in lettersWorthOne print 1
...
This is because the data structure you're using is a list (it is surrounded by [] brackets). The way to use these lists in your function is to see if they contain the letter in the list using the code:
if <variable> in <list> print <value>
The () brackets aren't doing anything, as mentioned in another answer so you can get rid of them.
This is just to explain to you why you don't see any result in the function you've written. The other answers which suggest using a dict (dictionary) are a better approach in practice.

Categories

Resources