My Json result from the API is as below
Json result:
"Issues": [{
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Rya"
},
{
"Key": "UserName",
"Value": "RH"
},
{
"Key": "Count",
"Value": "350"
}
]
},
{
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Mike"
},
{
"Key": "UserName",
"Value": "ML"
},
{
"Key": "Count",
"Value": "90"
}
]
}
]
I did a mapping by doing as below-
.Issues.map(o =>
o.Values.reduce((acc, {
Key,
Value
}) =>
(acc[Key] = Value, acc), {}));
The result of the mapping is as below-
{ "Display Name": 'Rya', "UserName" : "RH", value: 350 },
{ "Display Name": 'Mike', "UserName" : "ML", value: 90 }
Desired Result:
{ "Display Name": 'Rya', "UserName" : "RH" },
{ "Display Name": 'Mike', "UserName" : "ML"}
In my requirement, I want to ignore the last element as shown in the desired result.
The easiest solution is a combination of map, slice and reduce:
json.Issues.map(b =>
b.Values.slice(0, -1).reduce((c,d) => {
c[d.Key] = d.Value;
return c;
}, {}));
Demo:
let j = {
"Issues": [{
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Rya"
},
{
"Key": "UserName",
"Value": "RH"
},
{
"Key": "Count",
"Value": "350"
}
]
},
{
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Mike"
},
{
"Key": "UserName",
"Value": "ML"
},
{
"Key": "Count",
"Value": "90"
}
]
}
]
};
let r = j.Issues.map(b =>
b.Values.slice(0, -1).reduce((c, d) => {
c[d.Key] = d.Value;
return c;
}, {}));
console.log(r);
One solution is to add a filter before the reduce to filter out objects with the unwanted Count property.
.Issues.map(o =>
o.Values
.filter(({ Key }) => Key !== 'Count')
.reduce((acc, {
Key,
Value
}) =>
(acc[Key] = Value, acc), {}));
You could also do this filtering inline during the reduction by not adding objects when Key === 'Count'.
Note: There is no such thing as the last property in a JS object. It is a collection of properties whose actual order is implementation dependent and unreliable. For example, printing your object in different browsers and platforms could give any order whatsoever, nothing guarantees that consistency.
To remove the last element regardless of how many items in the array I would favor something like this:
let result = data.Issues.map(issue => {
let temp = issue.Values;
temp.splice(-1);
return temp.reduce((acc, {Key, Value}) => (acc[Key] = Value, acc), {});
});
Here's a fiddle
.Issues.map(o => o.Values.reduce((acc, {Key, Value}) => (Key !== 'Count') ? (acc[Key] = Value, acc) : acc, {}));
full code:
const j = {"Issues": [
{
"Id": null,
"Key": null,
"Values": [
{
"Key": "Display Name",
"Value": "Rya"
},
{
"Key": "UserName",
"Value": "RH"
},
{
"Key": "Count",
"Value": "350"
}
]
},
{
"Id": null,
"Key": null,
"Values": [
{
"Key": "Display Name",
"Value": "Mike"
},
{
"Key": "UserName",
"Value": "ML"
},
{
"Key": "Count",
"Value": "90"
}
]
}
]
}
const r = j.Issues.map(o => o.Values.reduce((acc, {Key, Value}) => (Key !== 'Count') ? (acc[Key] = Value, acc) : acc, {}));
console.log(JSON.stringify(r, null, 2))
Note that in a reduce method, the function is also passed in the current index and the array reduce is being called on. So if you want to ignore the last element of the array, you can do something like:
Issues.map(o =>
o.Values.reduce((acc, {Key, Value}, idx, arry) => {
if(idx < arry.length -1)
acc[Key] = Value;
return acc;
}, {}
);
Related
I'm looking to go from a JSON structure that looks something like this:
{
"id": "955559665",
"timestamp": "2022-04-21 00:00:19",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Safari/605.1.15",
"remote_addr": "123.456.789.012",
"read": "0",
"data": {
"80928111": {
"field": "80928111",
"value": "Z01234567",
"flat_value": "Z01234567",
"label": "ID",
"type": "text"
},
"90924321": {
"field": "90924321",
"value": {
"first": "Jane",
"last": "Doe"
},
"flat_value": "first = Jane\nlast = Doe",
"label": "Name",
"type": "name"
},
"88888770": {
"field": "88888770",
"value": "jdoe001#gmail.com",
"flat_value": "jdoe001#gmail.com",
"label": "Email",
"type": "email"
},
"12345678": {
"field": "12345678",
"value": "https://www.google.com/subdomain/attachment/file.txt",
"flat_value": "https://www.google.com/subdomain/attachment/file.txt",
"label": "Choose File",
"type": "file"
}
}
}
Ultimately to something like this:
{
"name_val":"Name: first = Jane\nlast = Doe\nEmail: jdoe001#gmail.com\n",
"file": {
"id": "12345678C",
"name": "file.txt"
}
}
In the original JSON, the 'data' object represents a form submission. Each sub object represents a field on the submitted form. The only distinction I'm interested in is the 'type' of field identified as 'file'.
Every response that is not of 'file' type, I want to concatenate into one large String value that looks like: 'label1: flat_value1\nlabel2: flat_value2...'
Note, the number of actual fields is variable.
Then I need a second object to show the field of type 'file', by identifying the 'field' id, and the name of the file.
I've gotten pieces of this to work. For example, using pluck and filter, I've been able to separate the types of fields.
Something like this:
%dw 2.0
output application/json
---
[
"fields": payload.data pluck(
{
"field": $."label",
"value": $."flat_value",
"type": $."type"
}
) filter ($."type" != "file") default "",
"files": payload.data pluck(
{
"type": $."type",
"fieldId": $."field"
}
) filter ($."type" == "file") default ""
]
Gives me:
[
{
"fields": [
{
"field": "ID",
"value": "Z01234567",
"type": "text"
},
{
"field": "Name",
"value": "first = Jane\nlast = Doe",
"type": "name"
},
{
"field": "Email",
"value": "jdoe001#gmail.com",
"type": "email"
}
]
},
{
"files": [
{
"type": "file",
"fieldId": "12345678"
}
]
}
]
And playing around with a modified JSON input, I was able to easily see concatenation similar to how I want to see it, but not quite there:
%dw 2.0
output application/json
var inputJson = [
{
"field": "ID",
"value": "Z01234567",
"type": "text"
},
{
"field": "Name",
"value": "first = Jane\nlast = Doe",
"type": "name"
}
]
---
inputJson map ((value, index) -> value.field ++ ': ' ++ value.value)
Gives me:
[
"ID: Z01234567",
"Name: first = Jane\nlast = Doe"
]
But I can't seem to put it all together and go from Beginning to End.
There are several ways to implement this. I recommend to try to encapsulate the parts that you get working and use them as building blocks.
%dw 2.0
output application/json
fun fields(x) = x.data pluck(
{
"field": $."label",
"value": $."flat_value",
"type": $."type"
}
) filter ($."type" != "file") default ""
fun files(x) = x.data pluck(
{
"type": $."type",
"fieldId": $."field"
}
) filter ($."type" == "file") default ""
---
{
name_val: fields(payload) reduce ((item,acc="") -> acc ++ item.field ++ ': ' ++ item.value ++ "\n"),
files: files(payload)[0]
}
Output:
{
"name_val": "ID: Z01234567\nName: first = Jane\nlast = Doe\nEmail: jdoe001#gmail.com\n",
"files": {
"type": "file",
"fieldId": "12345678"
}
}
I have an array of json objects like this
[
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "100"
},
{
"key": "validator",
"value": "sarah"
}
]
}
]
},
{
"events": [
{
"type": "message",
"attributes": [
{
"key": "action",
"value": "withdraw_reward"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "module",
"value": "distribution"
},
{
"key": "sender",
"value": "bob"
}
]
},
{
"type": "credit",
"attributes": [
{
"key": "recipient",
"value": "ross"
},
{
"key": "sender",
"value": "bob"
},
{
"key": "amount",
"value": "100"
}
]
},
{
"type": "rewards",
"attributes": [
{
"key": "amount",
"value": "200"
},
{
"key": "validator",
"value": "Ryan"
}
]
}
]
}
]
How to traverse through the types, check if it's type equals to rewards and then go through the attributes and verify if the validator equals to sarah and fetch the value of the key amount? Pretty new to scala and play framework. Any help would be great. Thanks
You could parse your JSON into a structure of case classes for easier handling and then extract the wanted field like so:
val json =
"""[
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"100"},
{"key":"validator","value":"sara"}
]}
]
},
{"events":[
{
"type":"message","attributes":[
{"key":"action","value":"withdraw_reward"},
{"key":"sender","value":"bob"},
{"key":"module","value":"distribution"},
{"key":"sender","value":"bob"}
]},
{
"type":"credit","attributes":[
{"key":"recipient","value":"ross"},
{"key":"sender","value":"bob"},
{"key":"amount","value":"100"}
]},
{
"type":"rewards","attributes":[
{"key":"amount","value":"200"},
{"key":"validator","value":"Ryan"}
]}
]
}
]
"""
case class EventWrapper(events: Seq[Event])
case class KeyValue(key: String, value: String)
case class Event(`type`: String, attributes: Seq[KeyValue])
import play.api.libs.json._
implicit val kvReads: Reads[KeyValue] = Json.reads[KeyValue]
implicit val eventReads: Reads[Event] = Json.reads[Event]
implicit val eventWrapperReads: Reads[EventWrapper] = Json.reads[EventWrapper]
val rewardAmountsValidatedBySara = Json
.parse(json)
.as[Seq[EventWrapper]]
.flatMap {
_.events.collect {
case Event(t, attributes) if t == "rewards" && attributes.contains(KeyValue("validator", "sara")) =>
attributes.collect {
case KeyValue("amount", value) => value
}
}.flatten
}
val amount = rewardAmountsValidatedBySara.head
For your example, rewardAmountsValidatedBySara would yield a List of Strings containing only the String "100". Which you could retrieve (potentially unsafe) with .head as shown above.
Normally you would not do this, as it could throw an exception on an empty List, so it would be better to use .headOption which returns an Option which you can then handle safely.
Note that the implicit Reads are Macros, which automatically translate into Code, that instructs the Play Json Framework how to read the JsValue into the defined case classes, see the documentation for more info.
My existing array is as below:
"Issues": [{
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Rya"
}, {
"Key": "UserName",
"Value": "RH"
}, {
"Key": "Count",
"Value": "350"
}]
}, {
"Id": null,
"Key": null,
"Values": [{
"Key": "Display Name",
"Value": "Mike"
}, {
"Key": "UserName",
"Value": "ML"
}, {
"Key": "Count",
"Value": "90"
}]
}]
My desired array:
[{
name: 'Rya',
value: 350
}, {
name: 'Mike',
value: 90
}]
What I tried:
Data.Issues.map(o=> o.Values.reduce((acc, {Key, Value}) =>
(acc[Key] = Value, acc), {}));
this.donughtChartData1 = this.donughtChartData.map( ({UserName, Count}) =>
({ name: UserName, value: Count}) );
But this gives me:
[{
"name": "RHanney",
"value": "350"
}, {
"name": "MLuckenbill",
"value": "90"
}]
This has quotes and my highcharts doesn't work if there are quotes.
In the last line of your code, add a + before Count so it gets converted to a number:
// ...
({ name: UserName, value: +Count}) );
How adding one character can bring the solution :-)
I am trying to convert my JSON result to an array to bind it to my Kendo controls.
The JSON result that I am getting is as follows.
"Issues": [
{
"Id": null,
"Key": null,
"Values": [
{
"Key": "Display Name",
"Value": "Rya"
},
{
"Key": "UserName",
"Value": "RH"
},
{
"Key": "Count",
"Value": "350"
}
]
},
{
"Id": null,
"Key": null,
"Values": [
{
"Key": "Display Name",
"Value": "Mike"
},
{
"Key": "UserName",
"Value": "ML"
},
{
"Key": "Count",
"Value": "90"
}
]
}
]
The array that I needed to bind it to the Kendo control is as below.
{ "Display Name": 'Rya', "UserName" : "RH", value: 350 },
{ "Display Name": 'Mike', "UserName" : "ML", value: 90 }
i)I dont want to hardcode the strings "Display Name", "User Name", "RH". I tried v.Values[0].Key: v.Values[0].Value, but it didn't work.
ii) Also I will not know how many "key, value" pairs will be present, so I need to loop through the Values and generate the array instead of fixed
category: v.Values[0].Value,
UserName: v.Values[1].Value,
value: v.Values[2].Value,
.
.
.
score: v.values[n].value
If you're using ES6, you don't really need lodash in this case:
var r = json.Issues.map(v => ({
category: v.Values[0].Value,
value: v.Values[2].Value,
}));
http://codepen.io/cjke/pen/RprJdG?editors=0010
If want to use lodash or not-ES6, then:
var r = _.map(json.Issues, function(v) {
return {
category: v.Values[0].Value,
value: v.Values[2].Value,
}
});
I've got the following JSON being sent to the server from the browser:
{
"title": "Testing again 2",
"abstract": "An example document",
"_href": "http://google.com",
"tags": [ "person" ],
"attributes": [ {
"id": 1,
"type": "TEXT",
"data": "test"
} ],
"sections": [ {
"id": 1,
"type": "LIST",
"data": [ {
"revision": 124,
"text": "test"
} ]
} ]
}
I need to make sure that the keys "_href", "id" and "revision" are not in the object anyplace at any level.
I found this but it doesn't quite work.
I searched npms.io and found has-any-deep which you can use after JSON.parse ing the JSON.
you need to parse json then check into the data
var str = '{
"title": "Testing again 2",
"abstract": "An example document",
"_href": "http://google.com",
"tags": [ "person" ],
"attributes": [ {
"id": 1,
"type": "TEXT",
"data": "test"
} ],
"sections": [ {
"id": 1,
"type": "LIST",
"data": [ {
"revision": 124,
"text": "test"
} ]
} ]
}';
var jsonObj = JSON.parse(str);
if ( typeof jsonObj._href == 'undefined') {
// check
}
A simple but not 100% foolproof solution would be to parse the JSON to string, and just search for your keys:
var a = JSON.stringify(JSONObject);
var occurs = false;
['"_href"', '"id"', '"version"'].forEach(function(string) {
if(a.indexOf(string) > -1) occurs = true;
});
The issue of course, is if there are values that match
'_href', 'id', 'version' in your JSON. But if you want to use native JS, I guess this is a good bet.
var a = {
"title": "Testing again 2",
"abstract": "An example document",
"tags": [ "person" ],
"attributes": [ {
"type": "TEXT",
"data": "test"
} ],
"sections": [ {
"type": "_href asdad",
"data": [ {
"text": "test"
} ]
} ]
},
b = {
"title": "Testing again 2",
"abstract": "An example document",
"_href": "http://google.com",
"tags": [ "person" ],
"attributes": [ {
"id": 1,
"type": "TEXT",
"data": "test"
} ],
"sections": [ {
"id": 1,
"type": "LIST",
"data": [ {
"revision": 124,
"text": "test"
} ]
} ]
},
aJson = JSON.stringify(a),
bJson = JSON.stringify(b);
var occursa = false, occursb = false;
['"_href"', '"id"', '"version"'].forEach(function(string) {
if(aJson.indexOf(string) > -1) { occursa = true};
});
['"_href"', '"id"', '"version"'].forEach(function(string) {
if(bJson.indexOf(string) > -1) { occursb = true};
});
console.log("a");
console.log(occursa);
console.log("b");
console.log(occursb);
You could use the optional second reviver parameter to JSON.parse for this:
function hasBadProp(json) {
let badProp = false;
JSON.parse(json, (k, v) => {
if ([_href", "id", "revision"].includes(k)) badProp = true;
return v;
});
return badProp;
}