I have a use case where there comes a JSON response from backend in the form as follows:
[
{
"name": "cab",
"child": [
{
"name": "def",
"child": [
{
"name": "ghi",
"power": "0.00",
"isParent": false
}
],
"power": "1.23",
"isParent": true
}
],
"power": "1.1",
"isParent": true
},
{
"name": "hhi",
"child": [
{
"name": "hhi2",
"child": [
{
"name": "hhi3",
"power": "0.00",
"isParent": false
}
],
"power": "1.23",
"isParent": true
}
],
"power": "1.1",
"isParent": true
}
]
I need to remove all objects that has power == 0. It's easy to use filter on simple collection of arrays, but there might be cases where any n number of childs can contain n number of childs in it.
Thanks in advance!
Just iterate over the arrays with a recursive function:
var json = ["JSON_HERE"];
function deleteIterator(json) {
if(json.power == "0.00") {
return null;
} else if(json.child) {
json.child = deleteIterator(json.child);
}
return json;
}
for(var i = 0; i < json.length; i++) {
json[i] = deleteIterator(json[i]);
}
What this does is:
Iterate over the JSON children.
Check if the power is "0.00".
If it is, remove it (return null)
Check if it has children
If it does, then iterate over it (go to step 2)
Return the JSON element.
Recursively iterate through the object, looking for child each time and filter on power === 0 or whatever your requirements are.
If you dont know how to use recursion, here is a tutorial to get you started. I really hope someone doesnt come along after me and hand you the exact solution to your problem because this is something you should be able to solve yourself once you know how to use recursion. You could also use loops but.. recursion is best.
Edit: This problem has been solved before, in a different flavor, but all the same. If you find your implementation ends up having bugs you cant figure out, please feel free to mention me in a new question and i'll try my best to help you.
You can iterate recursively using Array#filter with a named function expression:
var objArray = [{"name":"cab","child":[{"name":"def","child":[{"name":"ghi","power":"0.00","isParent":false}],"power":"1.23","isParent":true}],"power":"1.1","isParent":true},{"name":"hhi","child":[{"name":"hhi2","child":[{"name":"hhi3","power":"0.00","isParent":false}],"power":"1.23","isParent":true}],"power":"1.1","isParent":true}];
objArray = _.filter(objArray, function powerFilter(o) {
if (o.power == 0) return false;
if (o.isParent && o.child) {
o.child = _.filter(o.child, powerFilter); // recursive call
o.isParent = o.child.length > 0;
if (!o.isParent) delete o.child;
}
return true;
});
console.log(objArray);
<script src="https://cdn.jsdelivr.net/underscorejs/1.8.3/underscore-min.js"></script>
Related
I'm using Vue, lodash, etc.
{
"street": {
"id": "1",
"streetName": "test",
"buildings": [
{
"id": "1",
"buildingName": "test"
}
]
}
}
I have a setup similar to this. This is a singular object, I basically have an array of these.
All I get is a building.id value.
From it, I need to be able to find the building it belongs to, and there isn't any direct list of buildings for me to access.
Currently
I'm using a nested loop to loop through each site until I find the one that has a building with that id. I don't know if I'm doing it correctly, it doesn't feel correct.
for(var i = 0; i < streets.length; i++){
for(var x = 0; x < streets[i].buildings.length;x++){
if(streets[i].buildings[x].id == '2aec6bed-8cdd-4043-9041-3db4681c6d08'){
}
}
}
Any tips? Thanks.
You can use a combination of filter and some methods, like this:
var result = streets.filter(function(s) {
return s.street.buildings.some(function(b) {
return b.id === searchedId;
});
});
Using .some() method will return true if any building of the iterated buildings has the searchedId.
Using .filter() will filter the streets array to return only street object where the call of some() method on its buildings will return true, in other words which meets the condition of having an idequal to searchedId.
Demo:
var streets = [{
"street": {
"id": "1",
"streetName": "test",
"buildings": [{
"id": "1",
"buildingName": "test"
}]
}
}, {
"street": {
"id": "1",
"streetName": "test",
"buildings": [{
"id": '2aec6bed-8cdd-4043-9041-3db4681c6d08',
"buildingName": "test"
}]
}
}];
var searchedId = '2aec6bed-8cdd-4043-9041-3db4681c6d08';
var result = streets.filter(function(s) {
return s.street.buildings.some(function(b) {
return b.id === searchedId;
});
});
console.log(result);
If you're trying to get all the buildings in all streets by a buildingId, this solves the problem:
streetsList.map(streetItem => streetItem.street.buildings.find(building => building.id === searchedBuildingId)).filter(v => v);
.filter(v => v) is for filtering out falsy values since we want a clean result here.
If there can be more than a single building in a street with the same id, then use .some instead of .find in the example.
Presumably you have a streets object that contains street objects, like:
var streets = [
street :{ ... },
street :{ ... },
...
];
So you need to step into each street and iterate over the buildings. A for loop should be fairly efficient since it can return as soon as it finds the building. I don't think any of the built-in looping methods will do that.
The code in the OP won't work, as streets[i].buildings must be streets[i].streets.buildings and if(streets[i].buildings[x].id must be if(streets[i].street.buildings[x].id.
Below is a working for loop version, there's also a version using recent Array methods which are very much slower even on a very small dataset. According to jsperf, the for loop version is about 100 times faster in Safari, 10 times faster in Firefox and 50 times faster in Chrome.
I also think the for loop code is much more readable and therefore maintainable.
var streets = [{
"street": {
"id": "1",
"streetName": "test",
"buildings": [{
"id": "1",
"buildingName": "test"
}, {
"id": "2",
"buildingName": "test"
}]
}
}, {
"street": {
"id": "2",
"streetName": "test",
"buildings": [{
"id": "3",
"buildingName": "test"
}]
}
}
];
function getBldById(data, id) {
for (var i=0, iLen=streets.length; i<iLen; i++) {
var street = streets[i].street;
for (var j=0, jLen=street.buildings.length; j<jLen; j++) {
if (street.buildings[j].id == id) {
return street.buildings[j];
}
}
}
return null;
}
console.log(getBldById(streets, '1'));
function getBldById2(data, id) {
return data.map(streetObj =>
streetObj.street.buildings.find(building =>
building.id === id)
).filter(v => v)[0];
}
console.log(getBldById2(streets, '1'));
You might be missing street property, right?
I mean it should be: streets[i].street.buildings[x].id
I get 100 objects (JSON) from a website, which is build like this.
"object" : [{
"id": 12243,
"name": 'Carl',
"text": 'subheader',
"tags": ["cars","child", "blue"],
...
},
{
"id": 12245,
"name": 'Dark',
"text": 'subheader',
"tags": ["cars"],
...
}
...
]
I want to get only which has the tag child. How can I do it?
You could try something like this:
var objectsWithChild = json.object.filter(function(o){
return o.tags.includes("child");
});
where json is your json string.
Using Array.prototype.filter()
var objs = [{
"id": 12243,
"name": 'Carl',
"text": 'subheader',
"tags": ["cars","child", "blue"]
},
{
"id": 12245,
"name": 'Dark',
"text": 'subheader',
"tags": ["cars"]
}]
var objsWithTags = objs.filter(obj => obj.tags.indexOf('child') > -1);
console.log(objsWithTags);
Christos has a cleaner way to do it, but this way might be clearer to a beginner. One way to do it would be to use nested for loops.
var myObjects = []
for (var i = 0; i < data.object.length; i++) {
var ob = data.object[i];
for (var j = 0; j < ob.tags.length; j++) {
if (ob.tags[i] === "child") {
myObjects.push(ob);
}
}
}
There are probably multiple ways to implement this.
But the main ideas are mostly the same.
Here in textual form:
Define a container (an array for example) for your matched objects.
Loop through all objects.
Loop through all entries of your array "tags".
Check if you find your desired value "child".
Remember your found object by adding it to your container (array).
I have a JSON object that changes as it is appended with other objects throughout my application.
I have created a JavaScript function which behaves like indexOf but for a JSON array.
It looks like this:
// Find via slug
self.findSlug = function (array, slug) {
// Counter
var i = array.length;
// While i
while (i--) {
// If we find our slug, break the loop
if (array[i].slug === slug)
break;
}
// Return our counter
return i;
}
this works fine if the JSON object has a key called "slug". Now I would like to make it abiguous. i.e. something like:
if (array[i]["key-name"] === slug) break;
Here is an example of an array:
[
{
"title": "Hoody",
"price": 10,
"designable": true,
"configurable": true,
"slug": "hoody",
"fabric": "980506857489564534",
"font": "city-bold",
"fabrics": [
]
}
]
but that doesn't appear to work. Can someone suggest a way of doing this?
Cheers,
/r3plica
Please try this:
var array = {
1:{"title": "Hoody", "price": 10,"designable": true,"configurable": true,"slug": "hoody", "fabric": "980506857489564534",
"font": "city-bold", "fabrics": [ ] },
2:{"title": "Hoody2", "price": 20,"designable": true,"configurable": true,"slug": "hoody2", "fabric": "980506857489564534",
"font": "city-bold", "fabrics": [ ] }
}
for (key in array){
for (subkey in array[key]) {
if(subkey=="slug"){
//do something
}
}
}
Given:
appliedDoses = {
"_id": "MAIN",
"scheme": {
"_id": "MAIN_SCHEME",
"name": "ESQUEMA CLASICO",
"vaccines": [
{
"_id": "BCG",
"__v": 0,
"doses": [
{
"_id": "BCG_UNICA",
"frequencies": [
{
"_id": "BCG_UNICA_RECIEN_NACIDO",
"group_type": {
"_id": "RECIEN_NACIDO",
"name": "RECIEN NACIDO"
},
"__v": 0,
"status": true,
"number_applied": 10
}, ...
What I want is to filter by group_type.id == "RECIEN_NACIDO" and doses[]._id == "BCG_UNICA" then get a total sumatory of frequencies[].number_applied
I tried:
async.each(appliedDose, function(scheme){
async.each(scheme.scheme.vaccines, function(vaccine){
async.each(vaccine.doses, function(dose){
if(dose._id == getDose) {
async.each(dose.frequencies, function(frequencie){
if(frequencie.group_type._id == getGroup) {
applied += frequencie.number_applied;
}
});
}
});
});
});
But my code isn't quite efficiently and I was wondering if MapReduce can be used to improve it. Can somebody give me a hint? Help is much appreciated!
I'm a little confused by the object you're dealing with so I'll try to deal in abstraction. What you're probably looking to do here is easier and faster if you break it down.
get the collection that results from filtering by: group_type._id == "RECIEN_NACIDO" && doses._id
get the array of number_applied's by plucking them out with _.pluck
reduce that with sum: _.reduce(numAppliedArray, function(sum, x) { return sum + x}, 0);
I have to re-post this questions with more details again:
I got a JSON tree array.
The structure of JSON tree looks like this:
{
"app": {
"categories": {
"cat_222": {
"id": "555",
"deals": [{
"id": "73",
"retailer": "JeansWest"
}, {
"id": "8630",
"retailer": "Adidas"
}, {
"id": "11912",
"retailer": "Adidas"
}]
},
"cat_342": {
"id": "232",
"deals": [{
"id": "5698",
"retailer": "KFC"
}, {
"id": "5701",
"retailer": "KFC"
}, {
"id": "5699",
"retailer": "MC"
}]
}
}
}
}
now, I'd like to filter this JSON tree with var pattern="KF",
return all with retailer name contains KF with it's id.
======================update===========================
Just check my other question. It got solved.
filter multi-dimension JSON arrays
Use Array.filter or _.filter if you need to support IE < 9
Well, you can use _.filter:
var filteredArray = _.filter(arrayOfStrings, function(str) {
return str.indexOf(data) !== -1; });
... or jQuery.grep:
var filteredArray = $.grep(arrayOfStrings, function(str) {
return str.indexOf(data) !== -1; });
As you see, the approaches are quite similar - and, in fact, both use Array.filter, if it's available in the host environment.
Also note that the original array is not affected here. If you want otherwise, just assign the result of filtering to the same variable (i.e., arrayOfStrings in this example).