Javascript underscore data format array of object - javascript

By using underscoreJS lib and manipulating some datas, i have this object
var data = {
"2017-09-26": [
{
"id": 274281,
"value": 10
},
{
"id": 274282,
"value": 20
}],
"2017-09-27": [
{
"id": 274281,
"value": 12
},
{
"id": 274282,
"value": 13
}],
}
i would like to obtain this result below by passing the keys as date in the child object and transform the value of id key as the new key of the value of value
var data = [{
date:"2017-09-26",
274281: 10,
274282: 20
},
{
date:"2017-09-27",
274281: 12,
274282: 13
}]
Please does someone as an idea to help me to do this and ideally efficiently?
Thanks

Here it is in one line:
Object.keys(data).map(key => ({date: key, ...data[key].reduce((p, c) => {p[c.id] = c.value; return p}, {})}))
Result:
[{
"274281":10,
"274282":20,
"date":"2017-09-26"
},
{
"274281":12,
"274282":13,
"date":"2017-09-27"
}]

You need nested loops. The first level creates the objects with the date property, then you loop over the objects in that value, and add the id: value properties to the result.
var newdata = _.map(data, (date, objects) => {
res = {date: date};
_.each(objects, obj => {
res[obj.id] = obj.value;
});
return res;
});

You can use Array.from() on the result of Object.entries(data) to create an array of objects by passing a callback function as the second argument.
Then for each sub-array, use .reduce() to create a new object from its members.
var data = {
"2017-09-26": [
{ "id": 274281, "value": 10 },
{ "id": 274282, "value": 20 }
],
"2017-09-27": [
{ "id": 274281, "value": 12 },
{ "id": 274282, "value": 13 }
],
};
const result = Array.from(Object.entries(data), ([key, arr]) =>
arr.reduce((res, {id, value}) =>
Object.assign(res, {[id]: value})
, {date: key})
);
console.log(result);
Here's one that's just about the same, but uses the new object literal spread syntax.
var data = {
"2017-09-26": [
{ "id": 274281, "value": 10 },
{ "id": 274282, "value": 20 }
],
"2017-09-27": [
{ "id": 274281, "value": 12 },
{ "id": 274282, "value": 13 }
],
};
const result = Array.from(Object.entries(data), ([key, arr]) =>
({date: key,
...Object.assign({}, ...arr.map(({id, value}) => ({[id]: value})))
})
);
console.log(result);

Related

how to get max value from a nested json array

I have a nested json array and I am trying to get the maximum value of the points attribute in this array.
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
I want the max value of points from under the children sections. I mean from under technology and oil sectors.
What I've done so far:
var max;
for (var i in data.children.length) {
for (var j in data.data[i]) {
var point = data.data[i].children[j]
}
}
Try the following:
data = {
"name": "KSE100",
"children": [
{
"name": "TECHNOLOGY & COMMUNICATION",
"children": [
{
"name": "TRG",
'points': -21
},
{
"name": "SYS",
},
]
},
{
"name": "OIL",
"children": [
{
"name": "PPL",
'points': 9
},
{
"name": "PSO",
'points': -19
},
]
},
]
}
var array = [];
for (var first of data.children) {
for (var second of first.children) {
if(second.points != undefined)
{
array.push(second);
}
}
}
var maximumValue = Math.max.apply(Math, array.map(function(obj) { return obj.points; }));
console.log(maximumValue);
you can use the reduce method on the array object to do this
const maxValues = []
data.children.forEach(el => {
if (el.name === 'OIL' || el.name === 'TECHNOLOGY & COMMUNICATIO'){
const max = el.children.reduce((current, previous) => {
if (current.points > previous.points) {
return current
}
}, 0)
maxValues.append({name: el.name, value: max.points})
}
})
This will give you an array of the objects with the name and max value.
First you can convert your object to a string through JSON.stringify so that you're able to use a regular expression
(?<=\"points\":)-?\\d*
To matchAll the values preceded by the pattern \"points\": that are or not negative values. After it, convert the result to a array through the spread operator ... and then reduce it to get the max value.
const data = {name:"KSE100",children:[{name:"TECHNOLOGY & COMMUNICATION",children:[{name:"TRG",points:-21},{name:"SYS"}]},{name:"OIL",children:[{name:"PPL",points:9},{name:"PSO",points:-19}]}]};
console.log(
[ ...JSON.stringify(data).matchAll('(?<=\"points\":)-?\\d*')]
.reduce((acc, curr) => Math.max(curr, acc))
)
I wasn't 100% sure, what your exact goal is, so I included a grouped max value and and overall max value with a slight functional approach.
Please be aware that some functionalities are not working in older browsers i.e. flatMap. This should anyways help you get started and move on.
const data = {
name: "KSE100",
children: [
{
name: "TECHNOLOGY & COMMUNICATION",
children: [
{
name: "TRG",
points: -21,
},
{
name: "SYS",
},
],
},
{
name: "OIL",
children: [
{
name: "PPL",
points: 9,
},
{
name: "PSO",
points: -19,
},
],
},
],
};
const maxPointsByGroup = data.children.reduce(
(acc, entry) => [
...acc,
{
name: entry.name,
max: Math.max(
...entry.children
.map((entry) => entry.points)
.filter((entry) => typeof entry === "number")
),
},
],
[]
);
console.log("grouped max:", maxPointsByGroup);
const overallMax = Math.max(
...data.children
.flatMap((entry) => entry.children.flatMap((entry) => entry.points))
.filter((entry) => typeof entry === "number")
);
console.log("overall max:", overallMax);

Sort by the sum of array in object

I am looking for a solution to sort an array by the sum of an array property within an object.
For example if the main array is
[
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
How can I sort the sum of Day to return as
[
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
},
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
}
]
You just need sort your array with comparator, that uses reduce to calc sum of inner array values:
let arr = [{"Grid": {"Day": [11,12]}, "Name": "One"},
{"Grid": {"Day": [5,2]}, "Name": "Two"},
{"Grid": {"Day": [1,2]}, "Name": "Two"}];
let sum = el => el.Grid.Day.reduce((a,b) => a + b);
arr.sort((a,b) => sum(a) - sum(b));
console.log(arr)
You can use a combination of reduce to sum the array, and sort to order the output:
var input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
var result = input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(result);
function sumOfDay(obj){
return obj.Grid.Day.reduce( (acc,curr) => acc + curr, 0);
}
Note that Array.prototype.sort actually mutates the original array in place. so the above could also do
input.sort( (a,b) => sumOfDay(a) - sumOfDay(b));
console.log(input);
So, don't fall into the trap of thinking the original array is unchanged just because I assigned the result to result!.
If you do wish to sort a copy of the array do this:
var result = input.slice().sort( (a,b) => sumOfDay(a) - sumOfDay(b));
Create a new Array of a by mapping through it and using reduce on the Day Array of Grid to get your sum which you can compare within a sort to return your list sorted by summed days.
const a = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
]
const daySum = ({Grid}) => Grid.Day.reduce((prev, curr) => prev+curr, 0)
const sorted = [...a].sort(daySum)
console.log(sorted)
console.log(a) //Original array intact
Just "another" approach to solve the issue: assuming you (someday, later, eventually) may need to sort again, a good approach may also be to add a property to each grid item holding the sum of the days, avoiding the .reduce call every time you need to sort the array.
In this approach, .forEach is used to create the new property (through .reduce), and then .sort is used to sort the array in-place.
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
// Add a DaySum property evaluating the sum of the days.
input.forEach(i => i.Grid.DaySum = i.Grid.Day.reduce((a,b) => a + b));
// ^--- the second parameter (initial value) is unneeded here due to the fact that all elements are actually numeric, hence if the initial value is the first element of the array, which is a number already.
// Sor the array by that property.
input.sort((a,b) => a.Grid.DaySum - b.Grid.DaySum);
console.log(input);
Or, as suggested by #Andreas below, you can directly assign the property while sorting:
const input = [
{
"Grid": {
"Day": [
11,
12
]
},
"Name": "One"
},
{
"Grid": {
"Day": [
5,
2
]
},
"Name": "Two"
}
];
const sum = (a,b) => a + b;
input.sort((a,b) => {
a.Grid.DaySum = a.Grid.DaySum || a.Grid.Day.reduce(sum);
b.Grid.DaySum = b.Grid.DaySum || b.Grid.Day.reduce(sum);
return a.Grid.DaySum - b.Grid.DaySum;
});
console.log(input);

Is there a simple way to transform this JSON back and forth?

We are using Postman for our API testing. Some object we are getting back are very verbose and not easy to handle, so I want to create a helper method to make them a bit more concise. I know there are all kind of transformation libraries like node-json-transform, selecttransform, jsontransforms, etc., but unfortunately I can only use the Postman Sandbox libraries and vanilla JS.
I am looking for the simplest (least amount of loc and functions) way to transform this object:
var verbose = [
{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}];
into this:
var concise = {
"Name": "John Doe",
"Age": "33",
"Child": ["Jane", "Rocky"]
};
and back again into the verbose form.
I already tried the native way of foreach-ing over each object and adding properties/values to a new object, but it went ugly soon when I reached the multiple instance key/value pairs. I can imagine there is an easier way using map/reduce but I am unfamiliar with those methods.
Based on how I've understood your question, you want to create key-value pairs from your verbose array of objects. However, if there are key clashes, then the values should be converted into an array.
With that in mind, you will have to:
Use forEach to loop through your array of objects.
If key does not clash, we simply create a new key-value pair
If key clashes, then it gets a bit tricky:
If key clashes and this is the first occurrence, we convert the value in the key-value pair into an array
If key clashes and this is not the first occurrence, we know we are looking at an array
Now we definitely has an array, so we push our value into it
See proof-of-concept below:
var verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}];
var concise = {};
verbose.forEach(function(i) {
var key = i['Key'];
var value = i['Value'];
// If item exists, we want to convert the value into an array of values
if (key in concise) {
var item = concise[key];
// If it is not an array already, we convert it to an array
if (!Array.isArray(item))
item = [item];
item.push(value);
concise[key] = item;
}
// If item does not exist, we simply create a new key-value pair
else {
concise[key] = value;
}
});
console.log(concise);
Here, I assume all attributes are multivalued, then I reduce those that have length 1 to a simple value. This is a bit slower than the reverse approach, where you assume values are singlevalued and promote them to arrays when they prove otherwise, in order to respect the ordering imposed by Instance.
function makeConcise(verbose) {
let concise = {};
verbose.forEach(({Key, Value, Instance}) => {
if (!concise[Key]) concise[Key] = [];
concise[Key][Instance - 1] = Value;
});
Object.keys(concise).forEach(Key => {
if (concise[Key].length == 1) concise[Key] = concise[Key][0];
});
return concise;
}
The reverse function is similarly simple:
function makeVerbose(concise) {
let verbose = [];
Object.keys(concise).forEach(Key => {
if (Array.isArray(concise[Key])) {
concise[Key].forEach((Value, index) => {
verbose.push({Key, Value, Instance: index + 1});
});
} else {
verbose.push({Key, Value: concise[Key], Instance: 1});
}
});
return verbose;
}
const verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}
];
let concise = {};
verbose.forEach(item => {
const values = Object.values(item)
if (concise[values[0]]) concise = {...concise, [values[0]]: [concise[values[0]], values[1]]};
else concise = {...concise, ...{[values[0]]: values[1]}}
})
Try this. I have written both conversion functions.
I see other answers only provide only verbose to concise requirement.
let verbose = [{
"Key": "Name",
"Value": "John Doe",
"Instance": 1
},
{
"Key": "Age",
"Value": "33",
"Instance": 1
},
{
"Key": "Child",
"Value": "Jane",
"Instance": 1
},
{
"Key": "Child",
"Value": "Rocky",
"Instance": 2
}
]
let concise = {
"Name": "John Doe",
"Age": "33",
"Child": ["Jane", "Rocky"]
}
verboseToConcise = (verbose) => {
let obj = {}
verbose.forEach(v => {
let key = obj[v.Key]
if (key) typeof key === 'string' ? obj[v.Key] = [key, v.Value] : key.push(v.Value)
else obj[v.Key] = v.Value
})
return obj
}
conciseToVerbose = (concise) => {
let arr = []
Object.entries(concise).forEach(([key, value]) => {
if (typeof value === 'object') {
for (let i = 0; i < value.length; i++){
arr.push({
"Key": key,
"Value": value[i],
"Instance": i+1
})
}
} else {
arr.push({
"Key": key,
"Value": value,
"Instance": 1
})
}
})
return arr
}
console.log(verboseToConcise(verbose))
console.log(conciseToVerbose(concise))
You can do:
const verbose = [{"Key": "Name","Value": "John Doe","Instance": 1},{"Key": "Age","Value": "33","Instance": 1},{"Key": "Child","Value": "Jane","Instance": 1},{"Key": "Child","Value": "Rocky","Instance": 2}];
const concise = Object.values(verbose.reduce((a, {Key, Value}) => (Key === 'Child' ? a.childs[0].Child.push(Value) : a.keys.push({[Key]: Value}), a), {keys: [], childs: [{Child: []}]})).flat(1);
console.log(concise);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I also gave it a try using reduce:
EDIT: Without ... spread syntax, with Object.assign and array.concat
EDIT2: I wanted to try and turn it back again. In this code we lose the value of Instance:(
var verbose = [
{
Key: 'Name',
Value: 'John Doe',
Instance: 1,
},
{
Key: 'Age',
Value: '33',
Instance: 1,
},
{
Key: 'Child',
Value: 'Jane',
Instance: 1,
},
{
Key: 'Child',
Value: 'Rocky',
Instance: 2,
},
]
const concise = verbose.reduce(
(p, n) =>
Object.assign(p, {
[n.Key]: !p.hasOwnProperty(n.Key)
? n.Value
: typeof p[n.Key] === 'string'
? [p[n.Key], n.Value]
: p[n.Key].concat(n.Value),
}),
{},
)
console.log(concise)
// { Name: 'John Doe', Age: '33', Child: [ 'Jane', 'Rocky' ] }
const backAgain = Object.entries(concise).reduce(
(p, [k, v]) =>
Array.isArray(v)
? p.concat(v.map(x => ({ Key: k, Value: x })))
: p.concat({ Key: k, Value: v }),
[],
)
console.log(backAgain)
// [ { Key: 'Name', Value: 'John Doe' },
// { Key: 'Age', Value: '33' },
// { Key: 'Child', Value: 'Jane' },
// { Key: 'Child', Value: 'Rocky' } ]

React Native - get value of json depending on dynamic state

I have a json getting from API and store it into this.state.data like :
[
{
"name": "Primary Category",
"value": [
{
"value": "Fracture",
"Diagnosis_Code": ["DIAG003"],
"name": "Primary Category",
"FK_Diagnosis_Content_ID": 3,
"FK_Diagnosis_Category_ID": 1
},
{
"value": "Osteoarthritis",
"Diagnosis_Code": ["DIAG001"],
"name": "Primary Category",
"FK_Diagnosis_Content_ID": 1,
"FK_Diagnosis_Category_ID": 1
},
{
"value": "Osteonecrosis",
"Diagnosis_Code": ["DIAG002", "DIAG004"],
"name": "Primary Category",
"FK_Diagnosis_Content_ID": 2,
"FK_Diagnosis_Category_ID": 1
},
]
},
{
"name": "Anatomy",
"value": [
{
"value": "Hip",
"Diagnosis_Code": ["DIAG001"],
"name": "Anatomy",
"FK_Diagnosis_Content_ID": 4,
"FK_Diagnosis_Category_ID": 2
},
{
"value": "Pelvis",
"Diagnosis_Code": ["DIAG002", "DIAG003", "DIAG004"],
"name": "Anatomy",
"FK_Diagnosis_Content_ID": 6,
"FK_Diagnosis_Category_ID": 2
}
]
}
]
and I have a dynamic state using a function like this:
onChangeTextPress(key, value){
this.state.selected[key] = value
//another code
}
the example of this.state.selected is
[ 'Primary Category': 'Fracture', Anatomy: 'Hip']
I want to get FK_Diagnosis_Content_ID json depending on which this.state.selected[key] and it's value filled
so depending of selected example, I will have a result: [3, 4]
because json have a key called name and the value is Primary Category, and the value of selected state Primary Category is Fracture so I have a value of FK_Diagnosis_Content_ID in json, likewise about the Anatomy: 'Hip'
I don't know if you all know what I want, but feel free to asking me if you need another information,
hope someone can help me
You can get use of Object.keys() and Array.prototype.includes().
Sample 1
// Let's assume you have the below structure
const data = { 'Primary Category': 'Fracture', Anatomy: 'Hip' }
Object.keys(data).forEach((key) => console.log(key))
// Output:
// Primary Category
// Anatomy
So to get the desired value you can use something like below
Sample 2
// this.state.selected => { 'Primary Category': 'Fracture', Anatomy: 'Hip' }
const keys = Object.keys(this.state.selected);
const result = [];
this.state.data.forEach((d) => {
if(keys.includes(d.name)) {
d.value.forEach((v) => {
if(v.value === this.state.selected[d.name]) {
result.push(v['FK_Diagnosis_Content_ID']);
}
})
}
});
console.log(result);
// Output: [3,4]
Sample 3
// this.state.selected => [{ 'Primary Category': 'Fracture'}, {Anatomy: 'Hip' }]
// Since we know there is only one key in every object
const keys = this.state.selected.map((s) => Object.keys(s)[0])
const result = [];
this.state.data.forEach((d) => {
if(keys.includes(d.name)) {
d.value.forEach((v) => {
if(v.value === this.state.selected[keys.indexOf(d.name)][d.name]) {
result.push(v['FK_Diagnosis_Content_ID']);
}
})
}
});
console.log(result);
// Output: [3,4]
You can use this just as
const key = Object.keys(this.state.selected);
const response = [];
this.state.data.forEach((d) => {
if(key.includes(d.tag)) {
d.value.forEach((v) => {
if(v.value === this.state.selected[d.name]) {
result.push(v['FK_Diagnosis_Content_ID']);
}
})
}
});
console.log(result);

group by/formatting json data in Javascript

Sorry if this has been asked before. I have the JSON structure like:
{"data":[
{"Date":"03/04/2016","Key":"A","Values":"123"},
{"Date":"04/04/2016","Key":"A","Values":"456"},
{"Date":"03/04/2016","Key":"B","Values":"789"},
{"Date":"04/04/2016","Key":"B","Values":"012"}
]}
I want to change this to a different format which is basically grouped by Key and combines rest of the field in Values
{"Result":[
{
"Key":"A"
"Values":[["03/04/2016","123"], ["04/04/2016","456"]]
},
{"Key":"B"
"Values":[["03/04/2016","789"]},["04/04/2016","012"]]
}
]}
I want to do this javascript/html
You could iterate and build a new object if not exist.
var object = { "data": [{ "Date": "03/04/2016", "Key": "A", "Values": "123" }, { "Date": "04/04/2016", "Key": "A", "Values": "456" }, { "Date": "03/04/2016", "Key": "B", "Values": "789" }, { "Date": "04/04/2016", "Key": "B", "Values": "012" }], result: [] };
object.data.forEach(function (a) {
if (!this[a.Key]) {
this[a.Key] = { Key: a.Key, Values: [] };
object.result.push(this[a.Key]);
}
this[a.Key].Values.push([a.Date, a.Values]);
}, Object.create(null));
console.log(object);
I think this can be a better answer (but Nina's answer is the match for your problem terms) if items of data array have different properties and you don't want to change input data.
var raw = {"data":[
{"Date":"03/04/2016","Key":"A","Values":"123"},
{"Date":"04/04/2016","Key":"A","Values":"456"},
{"Date":"03/04/2016","Key":"B","Values":"789"},
{"Date":"04/04/2016","Key":"B","Values":"012"}
]};
var result = new Map;
raw.data.forEach(entry => {
var key = entry.Key;
if (this[key])
return this[key].push(getClonedData(entry));
this[key] = [getClonedData(entry)];
result.set(key, {
Key: key,
Values: this[key]
})
}, Object.create(null));
var filtered = {
result: [...result.values()]
}
console.log(filtered);
function getClonedData(entry) {
data = Object.assign({}, entry);
delete data.Key;
return data;
}

Categories

Resources