How to search all levels nested Javascript object - javascript

I have a Javascript Object that represents a nested JSON string like this:
http://pastebin.com/vwZb1XrA
There are many levels that contain a "name" element. For example "SVI" or "Population" or "Human Development Index".
I am trying to find a way to iterate over every level of the object to find the name "SVI" or "Population" or "Human Development Index". When a match is made I want to then replace the weight value with something like this:
if (data[key].name == name) {
data[key].weight = 55;
}

You can recursively check each and every object, like this
function rec(currentObject, values, replacement) {
if (values.some(function(currentValue) {
return currentObject.name === currentValue;
})) {
currentObject.weight = replacement;
}
(currentObject.children || []).forEach(function(currentItem) {
rec(currentItem, values, replacement);
});
}
rec(data, ["SVI", "Population", "Human Development Index"], 55);
console.log(data);
Working demo

You could use a recursive function, so if you just want to update the first object with a certain name, something like this:
function search(name, value, object) {
if (object.name == name) {
object.weight = value;
return;
}
if(object.children) {
var i = object.children.length;
while(i--) {
search(name, value, object.children[i]);
}
}
}
This would update without returning anything. If you remove the return statement it should update all objects with the name you specify.

You need a recursive function, that will go through each element and process its children. Something like this might work:
function ChangeWeightsOfName(arr, nameVal) {
for (var i = 0; i < arr.length; i++) {
var item = arr[i];
if (item.name === nameVal) {
item.weight = 42;
}
if (item.children && Array.isArray(item.children)) {
ChangeWeightsOfName(item.children, nameVal);
}
}
}
Your pasted JSON does not seem to be valid, so I included test one in this Fiddle

Related

How to compare the values of a particular key from both arrays using inArray in jQuery

I have two array with objects inside in it. I want to compare the values of a particular key from both arrays and do something with that. I have tried using inArray but couldn't succeed. Below is my code.
function initialize() {
geometry = 'http://yyyy.cartodb.com/api/v2/sql?q=SELECT name,ST_AsGeojson(the_geom) from msa_usa';
$.getJSON(geometry,
function(data) {
var place_names = [];
place_names.push({
name: "Abilene",
average: 8.65
});
for (i = 0; i < place_names.length; i++) {
if ($.inArray(data.rows[i].name, place_names[i].name) > -1) {
geom.push((data.rows[i].st_asgeojson));
average_value.push(place_names[i].average);
} else
(console.log("else"));
//console.log(place_names[i].name);
}
})
}
console.log(average_value.length);
console.log(geom.length);
I'm not sure why you're trying to use $.inArray() at all. It appears you're just trying to compare two properties on two objects (which happen to be in arrays, but you're already indexing to a particular object). If that's the case, then you can just directly compare the two properties:
if (data.rows[i].name === place_names[i].name)
But, in your code, you appear to have just created place_names so it will only have one value in it, not a whole bunch of values in it. So, now that confuses me as to what you're really trying to do here.
For more help, please describe in words what you're really trying to accomplish. Are you just trying to see if one particular .name property is in the data.rows array of objects?
If so, that would be a different piece of code like this:
function findPropInArray(array, propName, value) {
for (var i = 0; i < array.length; i++) {
if (array[i][propName] === value) {
return i;
}
}
return -1;
}
Then, in your code you could use something like this:
if (findPropInArray(data.rows, "name", "Abilene") !== -1) {
// "Abilene" was as a name property in data.rows
}

how to filter on multiple keys with OR condition

According to the docs,
{name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1".
I want to implement an or between those keys. for example it should return the rows which have property name containing "M" and also the rows having phone property containing "1".
Is there any expression by which I can do that? or I'll have to implement a custom filter for the same
you may try array filter method
list.filter(function(element){ return element.name.includes('M'); })
Can also try jquery
$.grep:
$.grep(list, function(element){ return element.name.includes('M'); })
Not sure if it's too early to conclude but it looks like creating a custom filter is the solution.
Here's it is,
.filter('orFilter', function () {
return function (list, filterOn) {
var out = [];
for (var i = 0; i < list.length; i += 1) {
var item = list[i];
for (var key in filterOn) {
var val = filterOn[key];
if (~item[key].indexOf(val)) {
out.push(item);
break;
}
}
}
return out;
}
});
I'll wait if someone posts a better solution.

Filter a store with array of values from one property with ExtJS

I'm trying to apply a constraint on combobox. It's half-working at the moment.
On the combobox, I have this listener:
[...]
listeners: {
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var getOrgValue = orgComboVal.getValue();
if (typeof getOrgValue !== undefined) {
reseaulist.clearFilter(true);
for (var q = 0, l = getOrgValue.length; q < l; q++) {
reseaulist.filter([
{property:'code_organisme', value: getOrgValue[q]}
]);
}
}
}
}
Ext.getCmp('Org1') defines another combobox.
When orgComboVal.getValue() is a single value, the filter is well applying.
but when it's an array of value, eg ["5", "9"], it's not working and the combobox supposed to be filtered shows no value (so I guess a filter is still applied but in an incorrect way).
I guess it's because the reseaulist.filter is called multiple time.
How can I achieve this ?
I saw the filterBy method but I don't know how to make it work.
Also, this post is interesting : How to filter a store with multiple values at once? but same, can't make it work since
getOrgValue.split(',')
is showing an error
(Object Array has no method split)
Any tips ? I'm using ExtJS 4.2.
EDIT
Thanks to #rixo, I've made it.
Also, I had to change some of the code he provided me, because the value of the Org1 combobox was always an array, even if empty, so the store filter was never cleared.
Here it is :
'focus': function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
console.log(values)
if (values != null) {
reseaulist.clearFilter(false);
if (Ext.isArray(values)) {
if (0 < values.length) {
reseaulist.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
reseaulist.clearFilter(true);
}
}
}
}
Each filter is applied one after the other on the previously filtered data set, so your code implements a logical AND. That's why all values are filtered out...
Here's an example using filterBy to accept any value that is in your array:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
store.clearFilter(false);
if (Ext.isArray(values)) {
store.filterBy(function(record, id) {
return Ext.Array.contains(values, record.get('code_organisme'));
});
} else {
record.get('code_organisme') === values;
}
} else {
store.clearFilter(true);
}
}
Or you could also use a regex with the filter method:
function (combo, value) {
var orgComboVal = Ext.getCmp('Org1')
var values = orgComboVal.getValue();
if (values != null) {
var filterValue = Ext.isArray(values)
? new RegExp('^(?:' + Ext.Array.map(values, function(value){return Ext.escapeRe(value)}).join('|') + ')$')
: values;
store.clearFilter(false);
store.filter('code_organisme', filterValue);
} else {
store.clearFilter(true);
}
}
Concerning your error, arrays indeed don't have a split method. Strings can be split into an array. Arrays, on their side, can be joined into a string...
Try This....
var getOrgValue = "5,9,4"; // array of value
reseaulist.filterBy(function(rec, id) {
return getOrgValue.indexOf(rec.get('code_organisme')) !== -1;
});

How can I make these two index-locating filters into one generic one?

I have two filters, findEdited and getUnitIndex. They both do exactly the same thing (find the index of an element in an array), but in different parts of an array. I would like to combine them into one filter, getIndex.
Here's the first one:
myApp.filter('findEdited', function(){
return function(food, foods) {
for (var index in foods) {
if (foods[index].uid == food.uid) {
return index;
}
}
return -1;
}
});
In the controller:
var index = $filter('findEdited')(food, $scope.editedFood);
And the second one:
myApp.filter('getUnitIndex', function () {
return function(list, item) {
for( var i = 0; i < list.length; i++ ) {
if( list[i].gram == item.gram ) {
return(i);
}
}
return(-1);
}
});
Controller:
var unitIndex = $filter('getUnitIndex')(food.unit, $scope.editedFood[index].unit.selected);
As near as I can tell, the only functional difference between them is the .uid & .gram identifier, which is telling the loop which part of the object to match. I've tried to rewrite these into one filter, like this, with ref standing in for this identifier:
myApp.filter('findIndex', function () {
return function(list, item, ref) {
for( var i = 0; i < list.length; i++ ) {
if( list[i].ref == item.ref ) {
return(i);
}
}
return(-1);
}
});
And called like this, if I want ref to be uid:
var unitIndex = $filter('findIndex')(food.unit, $scope.editedFood[index].unit.selected, 'uid');
This doesn't work. The example above returns 0 on every run. Any suggestions on how to pass the desired reference to this filter so that I can use it generically to find the index of any array item in any array?
Plunkr
Update
I can't get this to work for the filter "findEdited". I have written my generic filter like this:
myApp.filter('getIndex', function(){
return function(list, item, ref) {
for (var index in list) {
if (list[index][ref] == item[ref]) {
return index;
}
}
return -1;
}
});
Which works if call it like this, to find the index of a food unit by matching 'gram':
var unitIndex = $filter('getIndex')(food.unit, $scope.editedFood[index].unit.selected, 'gram');
But it doesn't work if I call it like this, to find out if a food unit exists in the array editedFood:
var foodIndex = $filter('getIndex')(food, $scope.editedFood, 'uid');
I can see that I am passing in different search objects & search contexts, but the foodIndex search works if I pass it to the almost-identical filter findEdited filter above. Any ideas why?
Here's an updated Plunkr.
You must use the array-like notation here (you can use it for objects as well). item.ref would mean the property called ref, while item[ref] will mean the property called whatever the expression in the [] evaluates to (in this case, whatever is stored in the variable called ref).
if( list[i][ref] == item[ref] ) {
In other words, writing item.ref is equivalent to writing item['ref'].

How to re-label Object in array?

Extending a previous question about JavaScript and jQuery, I'm trying to make an array and then look into it, but the array contains dimensions and I can't get it right..
var markers = [];
$.getJSON('GetList', "id"= + data.id,
function(data){
$.each(data.list, function(index, data) {
}
markers.push( {
category:data.category,
id:data.id,
location:{
latitude:data.location.latitude,
longitude:data.location.longitude
}
});
});
});
return markers;
}
The first thing that strikes me is that every item will now be called "Object", while default, I'm still wondering if they can be labeled instead?
Further down the line, I try to access the id to compare it to a selection by the user,
but it doesn't work.
var selection = $(div_id).val();
var arr = $.inArray(selection, markersArray.id);
if( arr >= 0) {
return search(selection, div_id);
} else {
throw("selection not found in selectionSearch()");
}
What am I doing wrong here..?
To label the objects, add a toString function, like this:
markers.push({
toString: function () { return this.something; },
category:data.category,
id:data.id,
location:{
latitude:data.location.latitude,
longitude:data.location.longitude
}
});
To search the array, you'll need your own loop.
jQuery's $.inArray function searches the array for an exact match; it cannot be used to find an object with a matching property.
You can find a match yourself, like this:
for(var i = 0; i < markers.length; i++) {
if (markers[i].id === selection)
return search(selection, div_id);
}
throw("selection not found in selectionSearch()");
The type of each item is Object, there is nothing strange about that. There is no reason to try to change that.
To filter out items from an array based on a property in the item, you use the grep method:
var arr = $.grep(markers, function(e){ return e.id === selection; });
To check if the array is empty, you don't compare the array to a number, you use the length property:
if (arr.length > 0) {

Categories

Resources