Deserialize possible children, which might have properties that needs to be deserialized - javascript

On my ASP.NET backend, I return an array of models, called Job, that can have n amount of jobs as children, using SignalR. One job could look like this:
{
"id": 0,
"json": '{"error": "Some error"}',
"children": [{
"id": 1
}, {
"id": 3,
"children": [{
"id": 4,
"json": '{"error": "Some other error"}'
}]
}]
}
As you can see, each job can have a child, which can have another child, and so on. Each job also has a json property, which is JSON in a text string. I want to deserialize these to a regular JavaScript object, which looks like this:
var deserialized = {
"id": 0,
"json": {
"error": "Some error"
},
"children": [{
"id": 1
}, {
"id": 3,
"children": [{
"id": 4,
"json": {
"error": "Some other error"
}
}]
}]
}
So basically goes like this:
If the job has a json property simply do job.json = JSON.parse(job.json)
If the job has children, loop over all the children
Repeat 1
How can I achieve this? I guess recursion is a way, but I'd rather see if it's possible to utilize the new ES6 methods.

1.If the job has a json property simply do job.json = JSON.parse(job.json)
2.If the job has children, loop over all the children
3.Repeat 1
Suppose, in a job you have both properties json with JSON string and also children then we have to execute both the points (1 & 2) one by one to convert the nested json property of the job into JSON Object.
In that case, first we have to convert the json property into JSON Object and then again we have to iterate the whole job with children array.
Try Array filter() method with ES6 Arrow function.
Working Demo :
let jobs = [{
"id": 0,
"json": '{"error": "Some error"}',
"children": [{
"id": 1
}, {
"id": 3,
"children": [{
"id": 4,
"json": '{"error": "Some other error"}'
}]
}]
},
{
"id": 1,
"json": '{"error": "Some error"}',
"children": [{
"id": 2
}, {
"id": 4,
"children": [{
"id": 5,
"json": '{"error": "Some other error"}'
}]
}]
}];
function parseObj(job) {
let res;
if (typeof job !== 'object') {
return;
} else {
res = job.filter(elem => (elem.json && typeof elem.json == 'string')?elem.json = JSON.parse(elem.json):parseObj(elem.children))
.filter(elem => (elem.json && typeof elem.json == 'string')?elem.json = JSON.parse(elem.json):parseObj(elem.children));
}
return res;
}
console.log(parseObj(jobs));

Related

JavaScript: filter array of objects by another

I'm trying to filter some objects based on another array of objects. So I'm getting data from an API. These are for example receipts:
[
{
"id": 1,
"name": "test",
"category": {
"id": 1,
"name": "Cookies",
},
},
{
"id": 2,
"name": "test2",
"category": {
"id": 2,
"name": "Candy",
},
}
]
Then I'm trying to filter the objects on the category name based on another array of categories.
I've created a function for this:
function onSelectCategory(category) {
let receiptsList = receipts.filter((a) =>
a.category.includes(category.name)
);
setReceiptsView(receiptsList);
setSelectedCategory(category);
}
const category = [ { "id": 2, "name": "Candy" } ];
onSelectCategory(category);
When I run this function, I get an empty Array []. I can't really figure out what I'm doing wrong.
Since the param seems to be an array of objects, you need to use Array#some for comparison instead:
const receipts = [
{ "id": 1, "name": "test", "category": { "id": 1, "name": "Cookies" } },
{ "id": 2, "name": "test2", "category": { "id": 2, "name": "Candy" } }
];
const categories = [ { "id": 2, "name": "Candy" } ];
const receiptsList = receipts.filter(({ category }) =>
categories.some(({ name }) => name === category.name)
);
console.log(receiptsList);
Another solution using Set:
const receipts = [
{ "id": 1, "name": "test", "category": { "id": 1, "name": "Cookies" } },
{ "id": 2, "name": "test2", "category": { "id": 2, "name": "Candy" } }
];
const categories = [ { "id": 2, "name": "Candy" } ];
const categorySet = new Set(categories.map(({ name }) => name));
const receiptsList = receipts.filter(({ category }) =>
categorySet.has(category.name)
);
console.log(receiptsList);
Assuming that category (the parameter) is a string, the issue is that you are attempting to get the attribute name from the string, when you should be comparing the string to the object.
Try this:
a.category.name == category;
instead of
a.category.includes(category.name)
I may be wrong aboout assuming that category is a string, please clarify by telling us what the parameter category is equal to.

Filter nested array in object array by array of values

Considering below object array:
[
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 1,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
},
{
"guid": "jHab6i",
"courses": [
{
"id": 4,
"name": "foobar",
}
]
},
{...}
]
I am trying to filter an object array, comparing IDs in the nested courses array with in the below array:
filter.courses = [1,3]
The following line works for the nth value in the array: (via https://stackoverflow.com/a/41347441/9766768)
let fil = filter(this.results, { courses: [{ id: this.filter.courses[n] }]});
However, I'm hoping to achieve this (pseudo code below):
let fil = filter(this.results, { courses: [{ id: this.filter.courses }]});
Expected output would be an array of objects containing any of the course IDs elements, in this case:
[
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 1,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
}
]
What would be considered the best solution in this case? Avoiding loops would be a bonus.
If you're trying to filter the elements whose course IDs contain in the filter.courses, you may use Array#every and Array#includes to do that:
const data = [{"guid":"j5Dc9Z","courses":[{"id":3,"name":"foo"}]},{"guid":"a5gdfS","courses":[{"id":1,"name":"bar"},{"id":3,"name":"foo"}]},{"guid":"jHab6i","courses":[{"id":7,"name":"foobar"}]}];
const courses = [1, 6, 3];
const r = data.filter(d => d.courses.every(c => courses.includes(c.id)));
console.log(r);
try this,
results = [
{
"guid": "j5Dc9Z",
"courses": [
{
"id": 3,
"name": "foo",
}
]
},
{
"guid": "a5gdfS",
"courses": [
{
"id": 1,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
}
]
var filter = [1]
console.log(results.map(result=>{
result.courses = result.courses.filter(course=>(filter.includes(course.id)))
return result
}))
Explore my recursive solution there: Playground Link
With this solution can nested array of objects being filtered from top level to botton level, layer by layer.
From what I understand the resulting array should contain all objects, that contain at least one course with an id that is contained in the array we use to filter.
So if an object exists with 2 courses - and one of them has an id we are looking for this object should then be part of the array that gets returned (see object with property "guid" : "a5gdfS" in the questions example)
With one little tweak the code provided in the answer by 31piy (marked as best by question owner) will do exactly what we desire. To do so we just change the array method every() to the array method some().
const r = data.filter(d => d.courses.every(c => courses.includes(c.id)));
const r = data.filter(d => d.courses.some(c => courses.includes(c.id)));
With the method every() the resulting array will only contain the objects, where each and every course has an id we are looking for. ("guid": "a5gdfS" is not in the resulting array)
With the method some() the resulting array will contain the objects, where at least one course has an id we are looking for ("guid": "a5gdfS" is in the resulting array)
/* arrays are the same as provided in the question
so that we can check against expected/desired output in the question */
const data = [{
"guid": "j5Dc9Z",
"courses": [{
"id": 1,
"name": "foo",
}]
},
{
"guid": "a5gdfS",
"courses": [{
"id": 2,
"name": "bar",
},
{
"id": 3,
"name": "foo",
},
]
},
{
"guid": "jHab6i",
"courses": [{
"id": 4,
"name": "foobar",
}]
}
];
const courses = [1, 3];
//array contains all objects with at least 1 course that has an id we are looking for
const r = data.filter(d => d.courses.some(c => courses.includes(c.id)));
console.log("code with some " + JSON.stringify(r));
//array contains only objects, whose classes all have an id we are looking for
const o = data.filter(d => d.courses.every(c => courses.includes(c.id)));
console.log("code with every " + JSON.stringify(o));
depending on what we are trying to do either every() or some() might be correct - depends on what we are trying to achieve

traverse from a specific point in json object and up to find all parents

I got a json object like this
{
"id": 1,
"name": "A",
"nodes": [
{
"id": 2,
"name": "B",
"nodes": [
{
"id": 3,
"name": "C",
"nodes": []
}
]
}
]
}
If I have as input the id of the object, lets take id: 3, how would I scan the whole three find the object with specific id and then scan upwards to the last parent.
So after the scan is done I know that C has parent B and B has parent A, so I can then print that like A-B-C
all based on me knowing the id of object I want to find parents of.
The above object can be of any lenght and can have many nodes and levels. So anyone has any idea how to traverse up the levels to top level if starting at specific level?
edit:
when I try to parse this
let data = [
{
"id": 1,
"name": "name",
"testing": "something",
"nodes": [
{
"id": 11,
"name": "name",
"testing": "something",
"nodes": []
}
]
},
{
"id": 2,
"name": "name",
"testing": "something",
"nodes": []
}
]
to json object by doing JSON.parse(data) I get an error
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
Also tried this
let jsonObject = JSON.stringify($scope.data);
jsonObject = JSON.parse(jsonObject);
createTree(jsonObject, null, nodeData.id)
and get different error:
TypeError: obj.nodes is not iterable
Do a basic DFS scan, add parent property along the way, and climb up when node found.
let jsonParsed = JSON.parse(`
{
"id": 1,
"name": "A",
"nodes": [
{
"id": 2,
"name": "B",
"nodes": [
{
"id": 3,
"name": "C",
"nodes": []
}
]
}
]
}
`)
let arr = []
function climbTree(obj) {
arr.unshift(obj.name)
if (obj.parent) {
climbTree(obj.parent)
}
}
function createTree(obj, parent = null, targetId = null) {
obj.parent = parent
if (targetId === obj.id) {
return climbTree(obj)
}
for (let node of obj.nodes) {
createTree(node, obj, targetId)
}
}
createTree(jsonParsed, null, 3)
console.log(arr.join('-'))

Filter multiple objects from array

I'm trying to filter an array of objects where the filter is another array (of integers) which are values of properties of the first array. I've managed to make it work but I'm not sure if it's the best way. Since I'm a beginner in javascript, I'd appreciate any suggestions/improvements.
The items.json file contains an object with an array of objects. I want to filter all the objects (within that array) that have an id equal to the "ids" on the itemsids array.
code:
const itemsall = require('./items.json');
let itemsids = [1, 403, 3];
let filtereditems = [];
itemsids.forEach(id => {
itemsall.items.forEach(item => {
if (id === item.id) {
filtereditems.push(item);
}
});
});
items.json (a small part of it)
{
"items": [
{
"id": 0,
"name": "Egg",
"img": "http://www.serebii.net/pokemongo/items/egg.png"
},
{
"id": 1,
"name": "Pokeball",
"img": "http://www.serebii.net/pokemongo/items/20pokeballs.png"
},
{
"id": 2,
"name": "Greatball",
"img": "http://www.serebii.net/pokemongo/items/greatball.png"
}
]
}
output: (expected)
[
{
"id": 0,
"name": "Egg",
"img": "http://www.serebii.net/pokemongo/items/egg.png"
},
{
"id": 403,
"name": "Cool Incense",
"img": "http://www.serebii.net/pokemongo/items/incense.png"
},
{
"id": 3,
"name": "Ultraball",
"img": "http://www.serebii.net/pokemongo/items/ultraball.png"
}
]
Thanks!
You can use filter() and indexOf() to return filtered array.
var data = {
"items": [{
"id": 0,
"name": "Egg",
"img": "http://www.serebii.net/pokemongo/items/egg.png"
}, {
"id": 1,
"name": "Pokeball",
"img": "http://www.serebii.net/pokemongo/items/20pokeballs.png"
}, {
"id": 2,
"name": "Greatball",
"img": "http://www.serebii.net/pokemongo/items/greatball.png"
}]
}
let itemsids = [1, 403, 3];
var result = data.items.filter(function(e) {
return itemsids.indexOf(e.id) != -1
})
console.log(result)
With ES6/ES7 you can use includes() like this.
var result = data.items.filter((e) => itemsids.includes(e.id));

Searching a Binary Tree in JavaScript [duplicate]

This question already has answers here:
how to iterate over inner objects / property in an object
(4 answers)
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 8 years ago.
I am a bit lost in JavaScript. I have this structure:
{
"value": 5,
"children": [{
"value": 18,
"children": [{
"value": 27,
"children": []
}, {
"value": 4,
"children": []
}]
}, {
"value": 2,
"children": []
}]
}
How can I get the highest value in the tree using JavaScript?
In this specific case you'd might want to use this:
var baseObject = {
"value": 5,
"children": [{
"value": 18,
"children": [{
"value": 27,
"children": []
}, {
"value": 4,
"children": []
}]
}, {
"value": 2,
"children": []
}]
};
function getHighestValue(obj) {
var res = obj.value;
for(var i in obj.children) {
res = Math.max(res, getHighestValue(obj.children[i]));
}
return res;
}
alert(getHighestValue(baseObject));
http://jsfiddle.net/qc9R4/1/
if understand you correct should look something like this.
var highestValue = JSONresponse.value;
HighVal(JSONresponse);
function HighVal(JSON)
{
if(highestValue < JSON.value)
{
highestValue = JSON.value
}
for(i=0;i<JSON.children.lenght;i++)
{
HighVal(JSON.children[i]);
}
}
Another way of doing this, if your object tree pattern is the same,
Stringify object and do regular expression to fetch all the integer values of this pattern "value: {n}" and then find the max value.
var jsono = {
"value": 5,
"children": [{
"value": 18,
"children": [{
"value": 27,
"children": []
}, {
"value": 4,
"children": []
}]
}, {
"value": 2,
"children": []
}]
}
var maxvalue;
JSON.stringify(jsono).match(/\"value\":\s*(\d+)/g).map(function(value){ return value.match(/(\d+)/g).map(function(value){ maxvalue = Math.max(maxvalue || 0,value);});});
alert(maxvalue);
http://jsfiddle.net/6R9p3/1/
I won't write the code for you, but basically, it's the same with any other language.
You traverse through every right child until you know that it's the end node.
How to use JavaScript to access JSON?
var tree =
{
parent : [
{
//child1 details
},
{
//child2 details
},
]
}
For JSON key access, use tree.<key> (dot) or tree['key']. In this case, tree.parent or tree["parent"].
For Array access, use indices. Since parent is an array, you can access the children by tree.parent[0] or tree['parent'][0].
I prefer the dot method though, to distinguish visually JSONs and arrays.
Also, you need something to distinguish a right child and a left child. You can either make it a convention to make the right child as the [0] index in the array, or you can add another value per node that says that its right or left.

Categories