Related
I have json data that looks like this:
{
"domain": {
"id": "40d74976-5db2-4a57-2a62-08d80969933d",
"name": "A",
"categories": {
"items": [
{
"id": "b17e674a-b64d-4224-bf93-016bc07b6349",
"name": "Category A",
"path": "A/Category A"
},
{
"id": "f6490fe4-98b0-4918-a419-0177000227c2",
"name": "Category AA",
"path": "A/Category A/Category AA"
},
{
"id": "092163a5-b96f-4453-8a22-0307c1f1abec",
"name": "Category B",
"path": "A/Category B"
},
{
"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a",
"name": "Category B-2",
"path": "A/Category B/Category B-2"
}
]
}
}
}
And i would like the result to be this:
{
"domain": {
"id" : "40d74976-5db2-4a57-2a62-08d80969933d",
"name": "A",
"categories": {
"items": [
{
"id": "b17e674a-b64d-4224-bf93-016bc07b6349",
"name": "Category A",
"path": "A/Category A"
"children": [
{
"id": "f6490fe4-98b0-4918-a419-0177000227c2",
"name": "Category AA",
"path": "A/Category A/Category AA"
}
]
},
{
"id": "092163a5-b96f-4453-8a22-0307c1f1abec",
"name": "Category B",
"path": "Domain A/Category B"
"children" :[
{
"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a",
"name": "Category B-2",
"path": "A/Category B/Category B-2"
}
]
}
]
}
}
}
I need to convert the json data in this way because i want to display this in the treeview format on my vue application but also retain the original id of each element since i want to implement the feature allow adding children to each specific element if the user needs to. To do that, it would need the original path of that element and the id of the element to which the child is being added
You can use a Map to key (a copy of) your items by their path attribute, and then iterate the items to link each of them with the parent (via lookup in the map).
Finally structure the top-level of the desired output.
Here is how that could be done:
let data = {"domain": {"id": "40d74976-5db2-4a57-2a62-08d80969933d","name": "A","categories": {"items": [{"id": "b17e674a-b64d-4224-bf93-016bc07b6349","name": "Category A","path": "A/Category A"},{"id": "f6490fe4-98b0-4918-a419-0177000227c2","name": "Category AA","path": "A/Category A/Category AA"},{"id": "092163a5-b96f-4453-8a22-0307c1f1abec","name": "Category B","path": "A/Category B"},{"id": "78ac6a08-4dc4-4f60-b0fd-0411b9631c4a","name": "Category B-2","path": "A/Category B/Category B-2"}]}}};
let root = {};
let { id, name, categories: { items } } = data.domain;
let map = new Map(items.map(o => [o.path, {...o}])).set(name, root);
items.forEach(o => {
let parent = map.get(o.path.slice(0, -o.name.length-1));
parent.children = (parent.children || []).concat(map.get(o.path));
});
let result = { id, name, categories: { items: root.children } };
console.log(result);
I currently have a Object which looks like so:
{
"Best Fare Description": {
"text": {
"value": "One",
"type": "TEXT"
}
},
"Brand ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Program ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Max Elapse Time": {
"integer": {
"value": 4,
"type": "INTEGER"
}
},
"Max Number of Connections": {
"integer": {
"value": 5,
"type": "INTEGER"
}
}
}
I am trying to iterate through the object and create an array of only the values. So for this object I would return an array of
["One","test","test",4,5]
What I've Tried:
data being the object
const tempList = [];
for (var key in data) {
for (var key2 in data[key]) {
for (var key3 in data[key][key2]) {
tempList.push(key3['value'])
}
}
}
However it seems like I am not doing something correct, as I get undefined or errors when I push into the array. Is there an easier/More Efficient way to accomplish this? Any help would be appreciated!
Because of the dynamic keys, you could take the values and map the last item.
var data = { "Best Fare Description": { text: { value: "One", type: "TEXT" } }, "Brand ID": { text: { value: "test", type: "TEXT" } }, "Program ID": { text: { value: "test", type: "TEXT" } }, "Max Elapse Time": { integer: { value: 4, type: "INTEGER" } }, "Max Number of Connections": { integer: { value: 5, type: "INTEGER" } } },
result = Object.values(data).map(o => Object.values(o)[0].value);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
The easiest thing is to loop over the Object.values of the two levels using map and reduce to build your array:
let obj = {"Best Fare Description": {"text": {"value": "One","type": "TEXT"}},"Brand ID": {"text": {"value": "test","type": "TEXT"}},"Program ID": {"text": {"value": "test","type": "TEXT"}},"Max Elapse Time": {"integer": {"value": 4,"type": "INTEGER"}},"Max Number of Connections": {"integer": {"value": 5,"type": "INTEGER"}}}
let arr = Object.values(obj).reduce((arr, item) => {
arr.push(...Object.values(item).map(inner => inner.value))
return arr
}, [])
console.log(arr)
Newer javascript engines will let you simplify a bit with flatMap:
let obj = {"Best Fare Description": {"text": {"value": "One","type": "TEXT"}},"Brand ID": {"text": {"value": "test","type": "TEXT"}},"Program ID": {"text": {"value": "test","type": "TEXT"}},"Max Elapse Time": {"integer": {"value": 4,"type": "INTEGER"}},"Max Number of Connections": {"integer": {"value": 5,"type": "INTEGER"}}}
let arr = Object.values(obj).flatMap(item => Object.values(item).map(inner => inner.value))
console.log(arr)
It seems you innermost object's structure stays the same. In that case, you could slightly change your existing code to do something like the following:
const tempList = [];
for (let key in data) {
for (let key2 in data[key]) {
tempList.push(data[key][key2].value);
}
}
Here's a recursive function that will do what you need (it will work with any shape of object):
const findValues => obj => Object.keys(obj).reduce((acc,key)=>{
if(key==='value'){
acc.push(obj[key])
}else if(typeof obj[key]==='object'){
acc.push(findValues(obj[key]))
}
return acc.flat()
},[])
So if your object is:
const obj = {
"Best Fare Description": {
"text": {
"value": "One",
"type": "TEXT"
}
},
"Brand ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Program ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Max Elapse Time": {
"integer": {
"value": 4,
"type": "INTEGER"
}
},
"Max Number of Connections": {
"integer": {
"value": 5,
"type": "INTEGER"
}
}
}
You would call it like this:
findValues(obj) // ["One", "test", "test", 4, 5]
A more generic version:
const findValues = selector => obj => Object.keys(obj).reduce((acc,key)=>{
debugger
if(key===selector){
acc.push(obj[key])
}else if(typeof obj[key]==='object'){
acc.push(findValues(selector)(obj[key]))
}
return acc.flat()
},[])
findValues('value')(obj) // ["One", "test", "test", 4, 5]
Codepen here: https://codepen.io/jenko3000/pen/yWWJxg
You're saying tempList.push(key3['value']), but key3 is a string, not an array. You also don't need 3 loops, you only need 2.
let data = {
"Best Fare Description": {
"text": {
"value": "One",
"type": "TEXT"
}
},
"Brand ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Program ID": {
"text": {
"value": "test",
"type": "TEXT"
}
},
"Max Elapse Time": {
"integer": {
"value": 4,
"type": "INTEGER"
}
},
"Max Number of Connections": {
"integer": {
"value": 5,
"type": "INTEGER"
}
}
}
const tempList = [];
for (var key in data) {
for (var key2 in data[key]) {
if (data[key][key2]['value'])
tempList.push(data[key][key2]['value'])
}
}
console.log(tempList);
I'm pretty new to Javascript, and I just learned about underscore.js. I have a deeply nested JSON object, and I need to use underscore to find key/value pairs, which I will then use to populate various HTML tables. If the structure was more shallow, using something like _.pluck would be easy, but I just don't know how to traverse past the first couple of nesting levels (i.e. surveyGDB, table, tablenames). The JSON object comes from an XML that is comprised of multiple nesting structures (mashed up from different database tables).
var JSONData =
"surveyGDB": {
"filename": "..\\Topo\\SurveyGeoDatabase.gdb",
"table": {
"tablename": [
{
"#text": "SurveyInfo\n ",
"record": {
"OBJECTID": "1",
"SiteID": "CBW05583-345970",
"Watershed": "John Day",
"VisitType": "Initial visit",
"SurveyInstrument": "Total Station",
"ImportDate": "2015-07-22T09:08:42",
"StreamName": "Duncan Creek",
"InstrumentModel": "TopCon Magnet v2.5.1",
"FieldSeason": "2015"
}
},
{
"#text": "QaQcPoints\n ",
"record": [
{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tp",
"Count": "357"
},
{
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tb",
"Count": "92"
},
{
"OBJECTID": "3",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "to",
"Count": "8"
},
{
"OBJECTID": "4",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bl",
"Count": "279"
},
{
"OBJECTID": "5",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bf",
"Count": "18"
}
]
},
{
"#text": "QaQcPolygons\n ",
"record": [
{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:43:08",
"SurveyExtentCount": "",
"WaterExtentCount": "",
"ChannelUnitsCount": "",
"ChannelUnitsUnique": ""
},
{
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T13:35:15",
"SurveyExtentCount": "1",
"WaterExtentCount": "1",
"ChannelUnitsCount": "21",
"ChannelUnitsUnique": "21"
}
]
}
]
}
}
}
For instance, I wanted all of the values for 'Code' in the 'QaQCPoints' table, so I tried:
var codes = _.flatten(_.pluck(JSONData.surveyGDB.table.tablename[1].record[0], "Code" ));
console.log(codes);
In the console, this returns an array with a length of 5, but with blank values.
What am I doing wrong?
I'd also rather search for the 'Code' values in the table based on something like the '#text' key value, instead of just using it's position in the object.
If I understood you correctly, you want to always search the record array within JSONData.surveyGDB.table.tablename array for some queries. This means you need to find the record based on some parameter and return something from the found record.
Do note that the record property is sometimes an array and sometimes an object (for table SurveyInfo) in your example so I'll assume you need to take this into account.
You can make a small function to extract data and handle both objects and arrays:
function extract(record, prop) {
if (Array.isArray(record)) {
return _.pluck(record, prop);
} else {
return record[prop];
}
}
Usage example:
I wanted all of the values for 'Code' in the 'QaQCPoints' table.
I'd also rather search for the 'Code' values in the table based on something like the '#text' key value, instead of just using it's position in the object.
To achieve this you first find a record using _.find, and then extract Code values from it using the method above:
var table = JSONData.surveyGDB.table.tablename;
// find an item that has `#text` property equal to `QaQcPoints`
var item = _.find(table, function(r) {
return r['#text'] === 'QaQcPoints';
});
// extract codes from the found item's record property
var code = extract(item.record, 'Code');
// output ["tp", "tb", "to", "bl", "bf"]
Running sample:
var JSONData = {
"surveyGDB": {
"filename": "..\\Topo\\SurveyGeoDatabase.gdb",
"table": {
"tablename": [{
"#text": "SurveyInfo",
"record": {
"OBJECTID": "1",
"SiteID": "CBW05583-345970",
"Watershed": "John Day",
"VisitType": "Initial visit",
"SurveyInstrument": "Total Station",
"ImportDate": "2015-07-22T09:08:42",
"StreamName": "Duncan Creek",
"InstrumentModel": "TopCon Magnet v2.5.1",
"FieldSeason": "2015"
}
}, {
"#text": "QaQcPoints",
"record": [{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tp",
"Count": "357"
}, {
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "tb",
"Count": "92"
}, {
"OBJECTID": "3",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "to",
"Count": "8"
}, {
"OBJECTID": "4",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bl",
"Count": "279"
}, {
"OBJECTID": "5",
"TIMESTAMP": "2015-07-22T09:18:43",
"Code": "bf",
"Count": "18"
}]
}, {
"#text": "QaQcPolygons",
"record": [{
"OBJECTID": "1",
"TIMESTAMP": "2015-07-22T09:43:08",
"SurveyExtentCount": "",
"WaterExtentCount": "",
"ChannelUnitsCount": "",
"ChannelUnitsUnique": ""
}, {
"OBJECTID": "2",
"TIMESTAMP": "2015-07-22T13:35:15",
"SurveyExtentCount": "1",
"WaterExtentCount": "1",
"ChannelUnitsCount": "21",
"ChannelUnitsUnique": "21"
}]
}]
}
}
}
function extract(record, prop) {
if (Array.isArray(record)) {
return _.pluck(record, prop);
} else {
return record[prop];
}
}
var table = JSONData.surveyGDB.table.tablename;
var item = _.find(table, function(r) {
return r['#text'] === 'QaQcPoints';
});
console.dir(item);
var code = extract(item.record, 'Code');
console.log(code);
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
You have a two stage problem. Stage one is figuring out which table is QaQcPoints. If that's always JSONData.surveyGDB.table.tablename[1], you're good.
The next stage is getting your data out. You can use native array manipulation most of the time (unless you're on really old browsers). So:
var table = JSONData.surveyGDB.table.tablename[1].record;
var codeArray = table.map(function(val) { return val.Code; });
Will do the trick.
I am getting this response from an API:
{
"statuses": {
"status": [
{
"name": "Member",
"id": "1"
},
{
"name": "Attender",
"id": "3"
},
{
"name": "Child",
"id": "4"
}
]
}
}
But I need to somehow flatten the response to be this:
{
"name": "Member",
"id": "1"
},
{
"name": "Attender",
"id": "3"
},
{
"name": "Child",
"id": "4"
}
How can I do that using Javascript?
var response = {
"statuses": {
"status": [
{
"name": "Member",
"id": "1"
},
{
"name": "Attender",
"id": "3"
},
{
"name": "Child",
"id": "4"
}
]
}
}
var statusObj = response.statuses.status;
$('#result').text('First name is: ' + statusObj[0].name)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label id="result"/>
You can do JSON.parse(str) and then you you just take the data from status[x]
If you really want to keep it as a string you can do
var content = str.match(/\[(.*?)\]/);
In fact, you just need to retrieve by response.statuses.status from your Javascript object.
But , If you needed to convert json to javascript object,
please use JSON.parse(your json response) method using JSON.js.
Download the JSON.js from https://github.com/douglascrockford/JSON-js
I need to delete item from an array with nested items. Here's the array,
{
"name": "Main Course",
"items": [
{
"menuname": "Chinese",
"id": "12",
"menu": [
{"name": "Noodles",id="1"},
{"name": "Rice",id="2"},
{"name": "Xinjiang Roast",id="3"}
]
},
{
"menuname": "Indian",
"id": "14",
"menu": [
{"name": "Rice",id="2"},
{"name": "Paratha",id="5"},
{"name": "Dal Fry",id="6"}
]
}
]
}
I need to delete the item say, {"name": "Rice",id="2"}, from the first menu (menuname:Chinese). Please note that the same item appears in another menu (menuname:Indian) too, which I don't want to delete. Looks like the common approach is to find the indexOf the item to delete and splice it. Here's what I tried,
myArray[0].items.forEach(function (val) {
if((val.menuname==="Chinese" && val.id==="12"))
{
val.menu.forEach(function (value) {
if(value.name === "Rice" && value.id==="2"){
myArray.indexOf(value.name);
}
})
}
});
This always returns an index of -1. What am I doing wrong?
Edit:A close vote for unclear question (Seriously?). The code I am using doesn't return the proper index and I asked what is wrong with the code. With all due respect, please spend some time reading the question.
First your JSON string look strange.
Then you can't call myArray[0] because you don't get an array:
var k = {
"name": "Main Course",
"items": [{
"menuname": "Chinese",
"id": "12",
"menu": [{
"name": "Noodles",
"id": "1"
}, {
"name": "Rice",
"id": "2"
}, {
"name": "Xinjiang Roast",
"id": "3"
}]
}, {
"menuname": "Indian",
"id": "14",
"menu": [{
"name": "Rice",
"id": "2"
}, {
"name": "Paratha",
"id": "5"
}, {
"name": "Dal Fry",
"id": "6"
}]
}]
};
k.items.forEach(function (val) {
if (val.menuname === "Chinese" && val.id === "12") {
for (var i = 0; i < val.menu.length; i++) {
if (val.menu[i].name === "Rice" && val.menu[i].id === "2") {
val.menu.splice(i, 1);
}
}
}
});
console.log(k);
http://jsfiddle.net/XmsQR/2/