Find multiple ocurences of a specific value in JSON - javascript

I have a complex JSON like this:
{
"a": {
"b": {
"c": {
"price": {
"type": "coin",
"value": "currency"
}
}
}
},
"e": {
"f": {
"price": {
"type": "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
}
}
I am trying to write a JavaScript function that will find "price" at any location and pull out the "type" and "value" for it. So my output should be:
"coin" : "currency"
"note" : "currency"
"cents" : "dollars"

You can create recursive function with for...in loop to return object as a result.
const data = {"a":{"b":{"c":{"price":{"type":"coin","value":"currency"}}}},"e":{"f":{"price":{"type":"note","value":"currency"}}},"price":{"type":"cents","value":"dollars"}}
function getPrice(data) {
const result = {}
for (let i in data) {
if (typeof data[i] == 'object') Object.assign(result, getPrice(data[i]))
if (i == "price") Object.assign(result, {
[data[i].type]: data[i].value
})
}
return result;
}
const result = getPrice(data);
console.log(result)

You could check the wanted key price and take type and value for a new object, or look deeper.
function flat(object) {
return Object.entries(object).reduce(
(r, [k, v]) => Object.assign(r, k === 'price' ? { [v.type]: v.value } : flat(v)),
{}
);
}
var object = { a: { b: { c: { price: { type: "coin", value: "currency" } } } }, e: { f: { price: { type: "note", value: "currency" } } }, price: { type: "cents", value: "dollars" } };
console.log(flat(object));

You need to recursively (or not) iterate on the properties of your object.
Here is some old-school javascript:
const testObj = {
"a":{
"b" : {
"c" :{
"price" : {
"type" : "coin",
"value" : "currency"
}
}
}
},
"e" : {
"f" : {
"price" : {
"type" : "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
}
};
function findOccurences(accum, obj, prop) {
if(obj.hasOwnProperty(prop)) {
accum.push(obj[prop]);
}
for(var p in obj) {
if(obj.hasOwnProperty(p) && p !== prop)
findOccurences(accum, obj[p], prop);
}
}
var accum = [];
findOccurences(accum, testObj, "price");
console.log(accum);

While the other answers are good, they don't allow different values for the same key. For example, if you have an additional price like this:
"g": {
"price": {
"type": "coin",
"value": "dollars"
}
}
It will overwrite the first value of coin with the other answers and you will end up with:
{
"coin": "dollars",
"note": "currency",
"cents": "dollars"
}
If you have that scenario and want to get the two different values of coin, you'll need to use a separate object for each key/value instead of making them properties of a single object:
var json = {
"a": {
"b": {
"c": {
"price": {
"type": "coin",
"value": "currency"
}
}
}
},
"e": {
"f": {
"price": {
"type": "note",
"value": "currency"
}
}
},
"price": {
"type": "cents",
"value": "dollars"
},
"g": {
"price": {
"type": "coin",
"value": "dollars"
}
}
};
function getPrice(data) {
var result = [];
for (let i in data) {
if (i == "price")
result.push({
[data[i].type]: data[i].value
});
else if (typeof data[i] == "object")
result.push(getPrice(data[i])[0]);
}
return result;
}
var price = getPrice(json);
console.log(price)

Related

How in JS to merge in one object two json objects where the ID of on object correspond on the same ID of the second object

My question relates to the fact I'm querying 2 different objects from DB and the result is in JSON. I need to merge them into one.
The 2 objects have in common this two key/value IRBId = ... and id = ... and they look as an example
OBJ 1
{
"data":{
"IRBs":{
"nodes":[
{
"id":"8",
"name":"Admin ",
},
{
"id":"9",
"name":"Again",
}
],
}
}
}
OBJ 2
{
"data":{
"informedConsentForms":{
"count":3,
"nodes":[
{
"id":"93",
...
"IRBId":"9",
},
{
"id":"92",
...
"IRBId":"8",
},
{
"id":"91",
...
"IRBId":"8",
}
],
}
},
As you will see above OBJ 2 and OBJ 1 corresponding with the same at IRBid and id.
What I need is to merge the two OBJ where IRBId OBJ 2 === id OBJ 1
The result I would expect after the merge is
OBJ merged
{
[{
"id":"93",
...
"IRBId":"9",
"irb": {
"name":"Again ",
...
}
},
{
"id":"92",
...
"IRBId":"8",
"irb": {
"name":"Admin ",
...
}
},
{
"id":"91",
...
"IRBId":"8",
"irb": {
"name":"Admin ",
...
}
],
},
I don't know how to make it looks like this.
Try using Array.reduce
Logic
Loop through second object data nodes
Find the matching nodes from object 1 data nodes.
Push to accumulator with required details. (I have added only the nodes that was mentioned in in Expected resut, you can add asmuch as you need.)
const obj1 = {
"data": {
"IRBs": {
"nodes": [
{
"id": "8",
"name": "Admin ",
},
{
"id": "9",
"name": "Again",
}
],
}
}
}
const obj2 = {
"data": {
"informedConsentForms": {
"count": 3,
"nodes": [
{
"id": "93",
"IRBId": "9",
},
{
"id": "92",
"IRBId": "8",
},
{
"id": "91",
"IRBId": "8",
}
],
}
},
};
const obj1List = obj1.data.IRBs.nodes;
const output = obj2.data.informedConsentForms.nodes.reduce((acc, curr) => {
const matchingNode = obj1List.find((item) => item.id === curr.IRBId);
if (matchingNode) {
acc.push({
id: curr.id,
IRBId: curr.IRBId,
irb: {
name: matchingNode.name
}
})
}
return acc;
}, []);
console.log(output);
You need to use the map function on the nodes in the first object to construct a new object that contains the second and first object's attributes.
const obj1 = {
"data": {
"IRBs": {
"nodes": [{
"id": "8",
"obj1": "one",
"name": "Admin ",
},
{
"id": "9",
"obj1": "two",
"name": "Again",
}
]
}
}
};
const obj2 = {
"data": {
"informedConsentForms": {
"count": 3,
"nodes": [{
"id": "93",
"obj2": "1",
"IRBId": "9",
},
{
"id": "92",
"obj2": "2",
"IRBId": "8",
},
{
"id": "91",
"obj2": "3",
"IRBId": "8",
}
],
}
}
};
const obj1Data = obj1.data.IRBs.nodes;
const obj2Data = obj2.data.informedConsentForms.nodes;
const res = obj2Data.map(item => {
const obj1Item = obj1Data.find(obj1Item => item.IRBId === obj1Item.id);
return obj1Item ? { ...item, "irb": { ...obj1Item}} : { ...item};
});
console.log(res);
i am using nested loop, try this one
const obj2 = {
"data":{
"informedConsentForms":{
"count":3,
"nodes":[
{
"id":"93",
"IRBId":"9",
},
{
"id":"92",
"IRBId":"8",
},
{
"id":"91",
"IRBId":"8",
}
],
}
},
}
const obj1 = {
"data":{
"IRBs":{
"nodes":[
{
"id":"8",
"name":"Admin ",
},
{
"id":"9",
"name":"Again",
}
],
}
}
}
const result = [];
const obj2Nodes = obj2.data.informedConsentForms.nodes;
for(let i = 0; i < obj2Nodes.length; i++) {
const obj1Nodes = obj1.data.IRBs.nodes
for(let j = 0; j < obj1Nodes.length; j++) {
if(obj2Nodes[i].IRBId === obj1Nodes[j].id) {
const {id, ...reObj1Nodes} = obj1Nodes[j];
result.push({
...obj2Nodes[i],
'irb': {
...reObj1Nodes
}
})
}
}
}
console.log(result)

Compare 2 nested JSON and highlight the differences them Javascript

I am working on a requirement where I need to compare 2 JSON Objects. Comparing JSON 1 and JSON 2 should be such that the Result JSON should be JSON1 keys and the values should be fetched from JSON2. The values should only be changed for same keys with different values. Also, I need to highlight only the changed values in the result. Here is my code where I have been able to compare and get the JSON result, I am only stuck with highlighting the changed values. May I know where did I go wrong or what is missing?
var compareJSON = function(obj1, obj2) {
var ret = {};
for(var i in obj2) {
if(!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
ret[i] = obj2[i];
}
}
return ret;
};
//JSON 1
var a = {
"name": [
"complex"
],
"dfts": [
{
"valuec": {
"valuesid": "1232"
},
"ids": {
"idp": "chanellp"
}
}
],
"container": {
"contid": "na",
"dpvalueus": {
"ftsme": "na"
},
"attributes": {
"channelpo": "na"
},
"item": [
{
"contid": {
"stjsk": "wher"
},
"quantity": "na",
"dpvalue": {
"valuers": "na"
}
}
]
}
};
//JSON 2
var b = {
"name": [
"simple"
],
"dfts": [
{
"valuec": {
"valuesid": "75756754"
},
"ids": {
"idp": "where"
}
}
],
"container": {
"contid": "360",
"dpvalueus": {
"ftsme": "100"
},
"attributes": {
"channelpo": "usual"
},
"item": [
{
"contid": {
"stjsk": "stkh"
},
"quantity": "1",
"dpvalue": {
"valuers": "wholesome"
}
}
]
}
};
console.log(compareJSON(a, b));
Result can be seen in console.
I would add the functionality that your are missing ( highlight the differences) here:
Change
if(!obj1.hasOwnProperty(i) || obj2[i] !== obj1[i]) {
ret[i] = obj2[i];
}
To:
if(!obj1.hasOwnProperty(i)) {
ret[i] = obj2[i];
continue;
}
if( obj2[i] !== obj1[i] )
{
obj2[i]->diffent = true;
ret[i] = obj2[i];
continue;
}
ret[i] = obj2[i];

Remove all the keys from nested object excluding one key in javascript

I have a JSON object as follows:
x = {
"prop1": {
"description": "prop1",
"dataType": "string",
"value" : "abc"
},
"prop2": {
"sub1": {
"description": "sub1",
"dataType": "integer",
"value" : 12
},
"sub2": {
"description": "sub2",
"dataType": "integer"
}
},
"prop3": {
"input": {
"name": {
"description": "input messages",
"dataType": "boolean",
"value": false
}
},
"output": {
"description": "output messages",
"dataType": "boolean",
"value": false
}
}
}
In the above object, I wanted to remove the keys(description, dataType), also remove the key if it doesn't have any value key. the expected output for the above one is as below.
y = {
"prop1": {
"value" : "abc"
},
"prop2": {
"sub1": {
"value" : 12
}
},
"prop3": {
"input": {
"name": {
"value": false
}
},
"output": {
"value": false
}
}
}
My current solution is as follows:
function findValue(obj, string, obj1) {
if (obj.hasOwnProperty("value")) {
obj1[string.substring(1)] = obj.value
return
}
for (var key in obj) {
findValue(obj[key], [string, key].join("."), obj1)
}
}
console.log(x);
var x1 = {}
findValue(x, "", x1)
var y = {};
function assign(obj, keyPath, value) {
const lastKeyIndex = keyPath.length - 1;
for (var i = 0; i < lastKeyIndex; ++i) {
const key = keyPath[i];
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = { "value": value };
}
Object.keys(x1).forEach(key => {
const keyPath = key.split('.');
let value = x1[key];
if (value != null) {
this.assign(y, keyPath, value);
}
});
console.log(y);
I did it in a very long way. I first convert my object into a format where each key is the combination of parent and child keys and then converted it into the expected format.
Is there any way to make it better?

Group and count values in an array

I have an array with objects, like the following.
b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
I want to count how many issues have status close, and how many have backlog. I'd like to save the count in a new array as follows.
a = [
{Name: 'Backlog', count: 1},
{Name: 'close', count: 2}
];
I have tried the following.
b.issues.forEach(function(i) {
var statusName = i.fields.status.name;
if (statusName in a.Name) {
a.count = +1;
} else {
a.push({
Name: statusName,
count: 1
});
}
});
That however doesn't seem to be working. How should I implement this?
This is a perfect opportunity to use Array#reduce. That function will take a function that is applied to all elements of the array in order and can be used to accumulate a value. We can use it to accumulate an object with the various counts in it.
To make things easy, we track the counts in an object as simply {name: count, otherName: otherCount}. For every element, we check if we already have an entry for name. If not, create one with count 0. Otherwise, increment the count. After the reduce, we can map the array of keys, stored as keys of the object, to be in the format described in the question. See below.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var counts = b.issues.reduce((p, c) => {
var name = c.fields.status.name;
if (!p.hasOwnProperty(name)) {
p[name] = 0;
}
p[name]++;
return p;
}, {});
console.log(counts);
var countsExtended = Object.keys(counts).map(k => {
return {name: k, count: counts[k]}; });
console.log(countsExtended);
.as-console-wrapper {
max-height: 100% !important;
}
Notes.
Array#reduce does not modify the original array.
You can easily modify the function passed to reduce to for example not distinguish between Backlog and backlog by changing
var name = c.fields.status.name;
into
var name = c.fields.status.name.toLowerCase();
for example. More advanced functionality can also easily be implemented.
Using ES6 Arrow functions you can do it with minimum syntax
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var countOfBackLog = b.issues.filter(x => {
return x.fields.status.name === "Backlog"
}).length
var countOfClose = b.issues.filter(x => {
return x.fields.status.name === "close"
}).length
a =[{Name: 'Backlog', count : countOfBackLog}, {Name: 'close', count : countOfClose}]
More about arrow functions here
You can write like this. It is dynamic.
var a = {};
for(var key in b["issues"]){
if(!a.hasOwnProperty(b["issues"][key].fields.status.name)){
a[b["issues"][key].fields.status.name] = 1;
}else{
a[b["issues"][key].fields.status.name] = a[b["issues"][key].fields.status.name]+1;
}
}
var c = [];
for(var key1 in a){
c.push({
name : key1,
count : a[key1]
});
}
Something like this should do the trick. Simply iterate over your data, keep 2 counters with the number of each type of issue, and create the data format you want in the end. Try it live on jsfiddle.
var b = {
"issues": [{
"fields": {
"status": {
"id": "200",
"name": "Backlog"
}
}
}, {
"fields": {
"status": {
"id": "202",
"name": "close"
}
}
}, {
"fields": {
"status": {
"id": "201",
"name": "close"
}
}
}]
};
var data = [];
for(var issue of b.issues){
var entryFound = false;
var tempObj = {
name: issue.fields.status.name,
count: 1
};
for(var item of data){
if(item.name === tempObj.name){
item.count++;
entryFound = true;
break;
}
}
if(!entryFound){
data.push(tempObj);
}
}
console.log(data);

Flatten Javascript object array [duplicate]

This question already has answers here:
reduce array to a by grouping objects with same property
(2 answers)
Closed 6 years ago.
I have an object as:
[
{
"DATA": "2016-01-22",
"TOTAL": "7"
},
{
"DATA": "2016-01-25",
"TOTAL": "3"
},
{
"DATA": "2016-01-26",
"TOTAL": "1"
},
{
"DATA": "2016-01-27",
"TOTAL": "2"
},
{
"DATA": "2016-01-22",
"TOTAL": "1"
},
{
"DATA": "2016-01-25",
"TOTAL": "1"
},
{
"DATA": "2016-01-27",
"TOTAL": "1"
},
...
]
How can I shrink it down to something like below, this is, concatenate/join the TOTAL keys where the date is the same and fill with 0 in case the date doesn't repeat?:
[
{
"DATA": "2016-01-22",
"TOTAL": ["7", "1"]
},
{
"DATA": "2016-01-25",
"TOTAL": ["3", "1"]
},
{
"DATA": "2016-01-26",
"TOTAL": ["1", "0"]
},
{
"DATA": "2016-01-27",
"TOTAL": ["2", "1"]
}
]
I've been trying with this block of code, but can't get TOTAL keys all the same dimension - filled with zeros would be fine.
var output = [];
d.forEach(function(value) {
var existing = output.filter(function(v, i) {
return v.DATA == value.DATA;
});
if (existing.length) {
var existingIndex = output.indexOf(existing[0]);
output[existingIndex].TOTAL = output[existingIndex].TOTAL.concat(value.TOTAL);
} else {
if (typeof value.TOTAL == 'string')
value.TOTAL = [value.TOTAL];
output.push(value);
}
});
console.log(JSON.stringify(output, null, 4));
var someData = [] // <- your instantiated array in question.
var transformedData = [];
var highestCount = 0;
someData.forEach(x => {
var foundIndex = transformedData.findIndex((ele) => ele.DATA === x.DATA);
if (foundIndex < 0) {
transformedData
.push({DATA : x.DATA, TOTAL : [x.TOTAL]});
} else {
transformedData[foundIndex]
.TOTAL.push(x.TOTAL);
var currentCountAtIndex = transformedData[foundIndex].TOTAL.length;
if (highestCount < transformedData[foundIndex].TOTAL.length) highestCount = currentCountAtIndex;
}
});
// fill any indicies in array that are lower than the highest count with 0
transformedData
.forEach(x => {
if (x.TOTAL.length < highestCount) {
while(x.TOTAL.length < highestCount) {
x.TOTAL.push(0);
}
}
});
It could be as simple as this:
var result = {};
var test = [
{
"DATA": "2016-01-22",
"TOTAL": "7"
},
{
"DATA": "2016-01-25",
"TOTAL": "3"
},
{
"DATA": "2016-01-26",
"TOTAL": "1"
},
{
"DATA": "2016-01-27",
"TOTAL": "2"
},
{
"DATA": "2016-01-22",
"TOTAL": "1"
},
{
"DATA": "2016-01-25",
"TOTAL": "1"
},
{
"DATA": "2016-01-27",
"TOTAL": "1"
}];
console.log("test array: ", test);
var len = 0,
sorted;
// Flatten the object.
test.forEach( d => {
result[d.DATA] == undefined ? result[d.DATA] = [d.TOTAL] : result[d.DATA].push(d.TOTAL);
});
// Sort so we get the max length to know how many zeros to add.
sorted = Object.keys(result).sort( (k, b) => {
return result[k].length - result[b].length;
});
// Max length from the sorted array.
len = result[sorted[sorted.length - 1]].length;
// push zeros
Object.keys(result).forEach( k => {
if(result[k].length < len){
for(var i = result[k].length; i < len; i++){
result[k].push("0");
}
}
});
console.log("result: ", result);

Categories

Resources