Immutable.js structure (updating nested map)? - immutable.js

I have a deeply nested immutable structure with maps and lists. I'm not sure about the best way to update a nested map within the structure (the node with id:356).
I create a function to search the list, I find the target map, I update it. But the structure stays intact!! any idea what I'm doing wrong?
https://jsbin.com/sadoni/1/edit?js,console
var structure = Immutable.fromJS(
{
someKey:{id:1},
links:[
{id:123, c:false, chd:[]},
{id:134, c:false, chd:[
{id:212, c:false, chd:[
{id:245, c:false, chd:[]},
{id:256, c:false, chd:[]}
]},
{id:145, c:false, chd:[]},
{id:156, c:false, chd:[]},
{id:213, c:false, chd:[
{id:313, c:false, chd:[]},
{id:314, c:false, chd:[
{id:345, c:false, chd:[]},
{id:356, c:false, chd:[]}
]}
]}
]}
]
}
);
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _findNode
function _findNode(nodes, func, cb){
let found = false;
var findInTree = (nodes, func) => {
if(nodes && nodes.size > 0){
nodes.forEach(node => {
if(found === false){
if (func(node) === true) {
found = true;
cb(node, nodes);
} else {
let chd = node.get('chd');
if(chd && chd.size > 0 && found === false){
findInTree(chd, func);
}
}
}
});
}
};
findInTree(nodes, func, cb);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _filter function
function filter(link){ return link.get('id')===356; }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - _find the links array inside the tree
var links = structure.get('links');
function changeTheNode(node, nodes){
console.log(nodes.get(1).toJS()); // log nodes array before the update
var index = nodes.findIndex(function(n){ return n === node; });
nodes = nodes.update(index, function(itm){ return itm.set('c', true); });
// nodes array changed
console.log(nodes.get(1).toJS());
// structure tree is still the same :(
//console.log(structure.toJS());
}
_findNode(links, filter, changeTheNode);

Recursion!
var data = Immutable.fromJS({
id:134, c:false, chd:[
{id:212, c:false, chd:[
{id:245, c:false, chd:[]},
{id:256, c:false, chd:[]}
]},
{id:145, c:false, chd:[]},
{id:156, c:false, chd:[]},
{id:213, c:false, chd:[
{id:313, c:false, chd:[]},
{id:314, c:false, chd:[
{id:345, c:false, chd:[]},
{id:356, c:false, chd:[]}
]}
]}
]
});
function search(val, target, changeTheNode) {
if (val.get('id') === target) {
return changeTheNode(val);
}
return val.set('chd',
val.get('chd')
.map((v) => search(v, target, changeTheNode))
);
}
var updatedData = search(data, 356, (node) => node.set('c', true))
The result is
{
"id": 134,
"c": false,
"chd": [
{
"id": 212,
"c": false,
"chd": [
{
"id": 245,
"c": false,
"chd": []
},
{
"id": 256,
"c": false,
"chd": []
}
]
},
{
"id": 145,
"c": false,
"chd": []
},
{
"id": 156,
"c": false,
"chd": []
},
{
"id": 213,
"c": false,
"chd": [
{
"id": 313,
"c": false,
"chd": []
},
{
"id": 314,
"c": false,
"chd": [
{
"id": 345,
"c": false,
"chd": []
},
{
"id": 356,
"c": true,
"chd": []
}
]
}
]
}
]
}

Related

Calculate min and max from json data and store the result as list

I have data in JSON format, the sample is given below. I want to calculate the min and max range for the values inside the variable i.e. align_X, align_Y, align_Z.
{"id": 0, "variable": {"align_X": 41, "align_Y": 51, "align_Z": 80}}
{"id": 1, "variable": {"align_X": 0}}
{"id": 2, "variable": {"align_Y": 1, "align_Z": 0}}
Desired output is:
"align_X": [
0.0,
41.0
],
"align_Y": [
1.0,
51.0
],
"align_Z": [
0.0,
80.0
]
Any help is appropriated.
Thank you
Can you try this
var test = [{
"id": 0,
"variable": {
"align_X": 41,
"align_Y": 51,
"align_Z": 80
}
}, {
"id": 1,
"variable": {
"align_X": 0
}
}, {
"id": 2,
"variable": {
"align_Y": 1,
"align_Z": 0
}
}
]
var result = test.reduce((acc, cobj) => {
for (key in cobj.variable) {
if (acc[key]) {
if(acc[key][0] > cobj.variable[key]){
if(!acc[key][1]){
acc[key][1] = acc[key][0];
}
acc[key][0] = parseFloat(cobj.variable[key]).toFixed(2)
} else if(acc[key][1] == undefined || acc[key][1] < cobj.variable[key]){
acc[key][1] = parseFloat(cobj.variable[key]).toFixed(2)
}
}
else {
acc[key] = [];
acc[key].push(parseFloat(cobj.variable[key]).toFixed(2))
}
}
return acc;
}, []);
console.log(result);
My solution
#Your input
d = [{"id": 0, "variable": {"align_X": 41, "align_Y": 51, "align_Z": 80}},
{"id": 1, "variable": {"align_X": 0}},
{"id": 2, "variable": {"align_Y": 1, "align_Z": 0}}
]
#To store result float(-inf) smallest number in python & float(inf) is largest
output = {
"align_X" : [float('inf'),float('-inf')],
"align_Y" : [float('inf'),float('-inf')],
"align_Z" : [float('inf'),float('-inf')]
}
for item in d:
for each in output.keys():
try:
if item['variable'][each]<output[each][0]:
output[each][0] = item['variable'][each]
if item['variable'][each]>output[each][1]:
output[each][1] = item['variable'][each]
except:
continue
print(output)

Google Sheets - Parse JSON string contained in one cell and extract specific values to another cell

I have a sheet where for each row in column Z there is a JSON string recovered from Twitter via TAGS.
The JSON strings in column Z all have a similar structure:
{
"hashtags": [
{
"text": "Negev_Summit",
"indices": [
172,
185
]
}
],
"symbols": [],
"user_mentions": [
{
"screen_name": "JY_LeDrian",
"name": "Jean-Yves Le Drian",
"id": 1055021191,
"id_str": "1055021191",
"indices": [
69,
80
]
}
],
"urls": [],
"media": [
{
"id": 1513588335893258200,
"id_str": "1513588335893258240",
"indices": [
271,
294
],
"media_url": "http://pbs.twimg.com/media/FQFYknkXoAAxgYd.jpg",
"media_url_https": "https://pbs.twimg.com/media/FQFYknkXoAAxgYd.jpg",
"url": "https://twitter.com/yairlapid/status/1513588345468825605",
"display_url": "pic.twitter.com/dA4cBepIh2",
"expanded_url": "https://twitter.com/yairlapid/status/1513588345468825605/photo/1",
"type": "photo",
"sizes": {
"medium": {
"w": 1024,
"h": 576,
"resize": "fit"
},
"thumb": {
"w": 150,
"h": 150,
"resize": "crop"
},
"large": {
"w": 1024,
"h": 576,
"resize": "fit"
},
"small": {
"w": 680,
"h": 383,
"resize": "fit"
}
}
}
]
}
I need to extract specific values for each JSON string in column Z and put them in columns AA, AB and AC (hashtags, user mentions, and URL's).
I've managed to achieve this with a really dirty multiple REGEXREPLACE formula but it doesn't seem logical that there is no way to fo this more efficiently:
=IFERROR("#"&JOIN(" #",SPLIT(REGEXREPLACE(REGEXREPLACE(REGEXREPLACE(REGEXREPLACE(REGEXREPLACE(REGEXEXTRACT(INDIRECT("Y"&ROW()),".*user_mentions\"":\[(.*)\],\""urls.*"),"(,\""indices\"":\[\d+,\d+\])",""),"(,\""id_str\"":\""\d+\"")",""),"(,\""id\"":\d+)",""),"(\{\""screen_name\"":\"")",""),"\"",\""name\"":\""(.){1,50}\""\}",""),",")),"")
Ideally i'm looking for a script which would parse the JSON string and extract 1 or more values from each section of the JSON. For example:
For hashtags (column AA):
=PARSEJSON(Z1, "hashtags")
Result:
#hashtag1 #hashtag2
For user_mentions (column AB):
=PARSEJSON(Z1, "user_mentions/screen_name")
Result:
#username1 #username2
Would appreciate any help sending me in the right direction.
If your main purpose is to only get the values in screen_name I'd modify my script and I'd use =IMPORTJSON(url, "user_mentions/screen_name")
/**
* Imports JSON data to your spreadsheet Ex: IMPORTJSON("http://myapisite.com","city/population")
* #param url URL of your JSON data as string
* #param xpath simplified xpath as string
* #customfunction
*/
function IMPORTJSON(url,xpath){
try{
var res = UrlFetchApp.fetch(url);
var content = res.getContentText();
var json = JSON.parse(content);
var patharray = xpath.split("/");
for(var i=0;i<patharray.length;i++){
json = json[patharray[i]];
}
if(typeof(json) === "undefined"){
return "Node Not Available";
} else if(typeof(json) === "object"){
var tempArr = [];
for(var obj in json){
tempArr.push([obj,json[obj]]);
}
return tempArr;
} else if(typeof(json) !== "object") {
return json;
}
}
catch(err){
return "Error getting data";
}
}
I managed to do it with a different script I found here.
This is the script:
function getData(json, path) {
const obj = JSON.parse(json);
const keys = path.split('.');
let current = obj;
for( key of keys ){
current = current[key];
}
return current;
}
You would then enter in the cell with =getData(Z1, "hashtags")
#Yiddy s answer did not work for me. So i did some modifications to it and came up with this.
function getData(range, path, sheet_name) {
var sprsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = sprsheet.getSheetByName(sheet_name);
var string = sheet.getRange(range).getValue();
var json = JSON.parse(string);
const keys = path.split('.');
var current = json;
for (key of keys) {
current = current[key];
}
return current;
}

How to read from a JSON with two keys

I have a json that I need to import and then return a certain value. The json has two keys, like
{
"NUM_High_Objects": {
"abseta_pt": {
"field1:[0.0,0.9]": {
"field2:[15,20]": {
"tagIso": 0.00012,
"value": 0.99
},
"field2:[20,25]": {
"tagIso": 0.00035,
"value": 0.98
}
},
"field1:[0.91,1.2]": {
"field2:[15,20]": {
"tagIso": 0.00013,
"value": 0.991
},
"field2:[20,25]": {
"tagIso": 0.00036,
"value": 0.975
}
},
"binning": [
{
"binning": [
0.0,
0.9,
1.2,
2.1,
2.4
],
"variable": "abseta"
},
{
"binning": [
15,
20,
25,
30,
40,
50,
60,
120
],
"variable": "pt"
}
]
}
},
What I need is to search if a pair of values is within the range of "field1" and "field2" and return the corresponding "value"
I tried following this Search nested json / dict for multiple key values matching specified keys but could not make it to work...
I ve tried something like
class checkJSON() :
def __init__(self,filein) :
self.good, self.bad = 0, 0
print 'inside json function : will use the JSON', filein
input_file = open (filein)
self.json_array = json.load(input_file)
def checkJSON(self,LS,run) :
try :
LSlist = self.json_array[str(run)]
for LSrange in LSlist :print LSrange, run
except KeyError :
pass
self.bad += 1
return False
CJ=''
CJ=checkJSON(filein='test.json')
isInJSON = CJ.checkJSON("0.5", "20")
print isInJSON
but this does not work as I am not sure how to loop inside the keys
If I am understanding your question correctly then the relevant portion of your JSON is:
{
"field1:[0.0,0.9]": {
"field2:[15,20]": {
"tagIso": 0.00012,
"value": 0.99
},
"field2:[20,25]": {
"tagIso": 0.00035,
"value": 0.98
}
},
"field1:[0.91,1.2]": {
"field2:[15,20]": {
"tagIso": 0.00013,
"value": 0.991
},
"field2:[20,25]": {
"tagIso": 0.00036,
"value": 0.975
}
},
"binning": [
{
"binning": [
0.0,
0.9,
1.2,
2.1,
2.4
],
"variable": "abseta"
},
{
"binning": [
15,
20,
25,
30,
40,
50,
60,
120
],
"variable": "pt"
}
]
}
Then the following code should do what you are trying to achieve. It doesn't look like you need to search for nested keys, you simply need to parse your field1[...] and field2[...]. The code below is a quick implementation of what I understand you are trying to achieve. It will return the value if the first parameter is in the range of a field1[...] and the second parameter is in the range of a field2[...]. Otherwise, it will return None.
import json
def check_json(jsondict, l1val, l2val):
def parse_key(keystr):
level, lrange = keystr.split(':')
return level, eval(lrange)
for l1key, l2dict in jsondict.items():
if 'field' in l1key:
l1, l1range = parse_key(l1key)
if l1val >= l1range[0] and l1val <= l1range[1]:
for l2key, vals in l2dict.items():
l2, l2range = parse_key(l2key)
if l2val >= l2range[0] and l2val <= l2range[1]:
return vals['value']
return None
Here is a driver code to test the implementation above.
if __name__ == '__main__':
with open('data.json', 'r') as f:
myjson = json.load(f)
print(check_json(myjson, 0.5, 20))

merge lists of dictionaries in terraform v0.12

I would like to do the following using terraform:
I have 2 JSONs:
1.json:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2",
"url": "url2",
"data": "data2",
"action": "action2"
},
{
"description": "description3",
"url": "url3",
"data": "data3"
}
]
2.json:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2_new",
"url": "url2",
"data": "data2_new"
},
{
"description": "description4",
"url": "url4",
"data": "data4"
}
]
and I want to merge them into one. Dictionaries from the second JSON should override dictionaries from the first one if url key is the same. I.e. combined JSON should look like:
[
{
"description": "description1",
"url": "url1",
"data": "data1"
},
{
"description": "description2_new",
"url": "url2",
"data": "data2_new"
},
{
"description": "description3",
"url": "url3",
"data": "data3"
},
{
"description": "description4",
"url": "url4",
"data": "data4"
}
]
Using python I can easily do it:
import json
with open('1.json') as f:
json1 = json.load(f)
with open('2.json') as f:
json2 = json.load(f)
def list_to_dict(json_list):
res_dict = {}
for d in json_list:
res_dict[d['url']] = d
return res_dict
def merge_json(json1, json2):
j1 = list_to_dict(json1)
j2 = list_to_dict(json2)
j1.update(j2)
res_list = []
for key in j1.keys():
res_list.append(j1[key])
return res_list
print(json.dumps(merge_json(json1, json2), indent=4))
How can I do that using terraform?
Using terraform 0.12.x
$ cat main.tf
locals {
# read from files and turn into json
list1 = jsondecode(file("1.json"))
list2 = jsondecode(file("2.json"))
# iterate over lists and turn url into a unique key
dict1 = { for item in local.list1 : item.url => item }
dict2 = { for item in local.list2 : item.url => item }
# combine both dictionaries so values converge
# only take its values
merged = values(merge(local.dict1, local.dict2))
}
output "this" {
value = local.merged
}
$ terraform apply
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
this = [
{
"data" = "data1"
"description" = "description1"
"url" = "url1"
},
{
"data" = "data2_new"
"description" = "description2_new"
"url" = "url2"
},
{
"data" = "data3"
"description" = "description3"
"url" = "url3"
},
{
"data" = "data4"
"description" = "description4"
"url" = "url4"
},
]
Terraform supports expanding a list into function parameters using the ... operator. This will allow an arbitrary number of documents to be read.
(I'm not sure, but I believe this feature was added in v0.15)
For this example, I added a new file 3.json with the contents:
[
{
"description": "description4_new",
"url": "url4",
"data": "data4_new"
}
]
For main.tf, I'm using the same logic as #someguyonacomputer's answer:
$ cat main.tf
locals {
jsondocs = [
for filename in fileset(path.module, "*.json") : jsondecode(file(filename))
]
as_dicts = [
for arr in local.jsondocs : {
for obj in arr : obj.url => obj
}
]
# This is where the '...' operator is used
merged = merge(local.as_dicts...)
}
output "as_list" {
value = values(local.merged)
}
Result:
Changes to Outputs:
+ as_list = [
+ {
+ data = "data1"
+ description = "description1"
+ url = "url1"
},
+ {
+ data = "data2_new"
+ description = "description2_new"
+ url = "url2"
},
+ {
+ data = "data3"
+ description = "description3"
+ url = "url3"
},
+ {
+ data = "data4_new"
+ description = "description4_new"
+ url = "url4"
},
]
References:
Terraform Docs -- Function Calls # Expanding Function Arguments

Es6: Create an array of objects from a json

I have a json in the below format.
[
{"id": 1,
"name": "peter" },
{"id": 2,
"name": "john" },
{"id": 3,
"name": "justin" }
.
.
{"id": 500,
"name": "david" },
]
I am trying to create an array in batches of 10 in the below format
[
{
{"id": 1,
"name": "peter" },
.
.
{"id": 10,
"name": "nixon" },
},
{
{"id": 11,
"name": "nancy" },
.
.
{"id": 20,
"name": "underwood" },
}
.
.
]
I tried using reduce and tried for loop to loop through it, but was unsuccessful
Here's a demo.
const str = "abcdefghigklmnopqrstuvwxyz";
let data = [];
for(let i = 0; i < 26; i++){
data.push({id : i, name: str.charAt(i)});
}
let res = data.reduce((acc, d) => {
let groupId = Math.floor(d.id / 10);
acc[groupId] = acc[groupId] || {};
acc[groupId][d.id] = d;
return acc;
}, {});
console.log(Object.values(res));
If you can ensure that id is the same sequence as their position in array, i think simply slice will better.