Delete JavaScript object attributes like - javascript

Here is what I need to do. I have an object that goes
{"MainASubB":"AB","MainBSubC":"BC"...}
Every once in a while I need to take out all attributes that start MainA prior to putting in a new attribute starting MainA. In the example above the object transformations would be
{"MainASubB":"AB","MainBSubC":"BC"...} =>
{"MainBSubC":"BC"...} => //MainASubB has now been taken out
{"MainASubD":"AB","MainBSubC":"BC"...}; A new MainA group attribute, MainASubD has now been added.
I am aware of what Javascript delete can do but on its own I dont think it quite takes me all the way there. I should mention that
At times there may be no MainA group attribute present in the first place.
Provided the code works as intended there can never be more than one MainA group attribute.
Stringifying the object, cleaning out the string as required, then de-stringifying it and then finally putting in the new MainA group attribute is certainly possible but I am wondering if there is another techique, perhaps one reliant on jQuery?, that will get me there faster.

You have to iterate over the properties, compare each name and then delete the property:
for (var prop in obj) {
if (prop.indexOf('MainA') === 0) { // property name starts with 'MainA'
delete obj[prop];
break; // since there can be only one
}
}
I wouldn't use such "hierarchical" property names though. Why not use nested objects and just overwrite the value as you see fit?
For example:
var obj = {
MainA: {
SubA: '...'
},
MainB: {
SubA: '...'
}
};
and then it's just:
obj.MainA = {SubD: '...'};
or just add the "sub" value:
obj.MainA.SubD = '...';
This would be more flexible in the long run.

Here is a generic function :
function removeProperties (obj, prop) {
Object.keys (obj).forEach (
function (p) {
if (typeof prop === 'string' ? p.indexOf (prop) == 0 : prop.test (p))
delete obj[p];
});
return obj;
}
The parameter obj is the object wheich you want to remove properties from. Parameter prop can be a string, 'MainA' for example, in which case any properties with names starting with that string will be removed. If 'prop is a regular expression then any properties whose names match it will be removed.
The removal is done in-place, i.e obj itself is modified, it is also returned as the result of the function.
See it in action here : http://jsfiddle.net/jstoolsmith/EpSxC/

Related

Check if object already exists in object

I want to check if an object already exists in a given object by only having the object.
For instance:
const information = {
...
city: {
Streetname: ''
}
}
Now, I get the city object and want to check if it is already in the information object (without knowing the property name). The city could be n deep in the information object.
To get the property name of an object you can use Object.keys(). The first problem solved.
Now we need to iterate through the whole object including nested objects. This is the second problem.
And compare it to a query object. This is the third problem.
I assume that we have an object that only contains "simple" though nested objects with primitive values (I do not consider objects with functions or arrays)
// let's assume we have this object
const information = {
city: {
Streetname: 'streetname1'
},
house: {
color: "blue",
height: 100,
city: {
findMe: { Streetname: '' } // we want to get the path to this property 'findMe'
}
},
findMeToo: {
Streetname: '' // we also want to get the path to this proeprty 'findMeToo'
},
willNotFindMe: {
streetname: '' // case sensetive
}
}
// this is our object we want to use to find the property name with
const queryObject = {
Streetname : ''
}
If you use === to compare Objects you will always compare by reference. In our case, we are interested to compare the values. There is a rather extensive checking involved if you want to do it for more complex objects (read this SO comment for details), we will use a simplistic version:
// Note that this only evaluates to true if EVERYTHING is equal.
// This includes the order of the properties, since we are eventually comparing strings here.
JSON.stringify(obj1) === JSON.stringify(obj2)
Before we start to implement our property pathfinder I will introduce a simple function to check if a given value is an Object or a primitive value.
function isObject(obj) {
return obj === Object(obj); // if you pass a string it will create an object and compare it to a string and thus result to false
}
We use this function to know when to stop diving deeper since we reached a primitive value which does not contain any further objects. We loop through the whole object and dive deeper every time we find a nested object.
function findPropertyPath(obj, currentPropertyPath) {
const keys = isObject(obj) ? Object.keys(obj) : []; // if it is not an Object we want to assign an empty array or Object.keys() will implicitly cast a String to an array object
const previousPath = currentPropertyPath; // set to the parent node
keys.forEach(key => {
const currentObj = obj[key];
currentPropertyPath = `${previousPath}.${key}`;
if (JSON.stringify(currentObj) === JSON.stringify(queryObject)) console.log(currentPropertyPath); // this is what we are looking for
findPropertyPath(currentObj, currentPropertyPath); // since we are using recursion this is not suited for deeply nested objects
})
}
findPropertyPath(information, "information"); // call the function with the root key
This will find all "property paths" that contain an object that is equal to your query object (compared by value) using recursion.
information.house.city.findMe
information.findMeToo
const contains = (item, data) => item === data || Object.getOwnPropertyNames(data).some(prop => contains(item, data[prop]));
const information = {
city: {
Streetname: ''
}
}
console.log(contains(information.city, information));
console.log(contains({}, information));

Angularjs filter only first level properties

I have javascript object which look like this:
{ name: 'Barney', color: 'blue', parent: {name: 'Henry'} }
When I use $filter('filter')('Henry') on an array which includes the object above, I don't want it to be included as a result. I only want to filter out things matching on the first level, in this case the 'name' and 'color' properties.
Is it possible?
You'd want to create a custom filter since the default filter provided by Angular appears to do a deep comparison.
Here's an example I came up with real quick, you may want to change the filter to suit your needs:
// Looks like a nice little tree :)
app.filter('shallowFilter', function () {
return function (items, value) {
if (!angular.isDefined(value) || value === '') {
return items;
}
return items.filter(function (item) {
for (var prop in item) {
if (item.hasOwnProperty(prop)) {
var propVal = item[prop],
propLower,
valLower;
// Skip values that are not a string..
if (typeof propVal !== 'string') {
continue;
}
propLower = propVal.toLowerCase();
valLower = value.toLowerCase();
if (propLower.indexOf(valLower) !== -1) {
return true;
}
}
}
});
};
});
Here's a plunker demonstrating how it works.
Edit:
This will only loop over the "low level" properties of an object (shallow search), which is what I assume you want.
Use the object notation:
From documentation:
Object: A pattern object can be used to filter specific properties on objects contained by array. For example {name:"M", phone:"1"} predicate will return an array of items which have property name containing "M" and property phone containing "1". A special property name $ can be used (as in {$:"text"}) to accept a match against any property of the object. That's equivalent to the simple substring match with a string as described above. The predicate can be negated by prefixing the string with !. For Example {name: "!M"} predicate will return an array of items which have property name not containing "M".
$filter('filter')({ name: 'Henry' });

Angular $watch object and get property name

I want to $watch an object for changes on any of its properties and, when any of them changes, get its name (apart from newValue and oldValue).
Is that possible?
This question is two years old but I just ran into the same issue and I thought i'd share my solution to it. Back in the 1.2 version you used to be able to access the changed property by referencing this.exp in the watch function, but that was deprecated presumably for performance reasons. The easiest way to get the changed property is to loop through the new and old objects and compare the values by property name.
$scope.settings = {
'opt1' : 'something',
'opt2' : 'something else',
'opt3' : 'another something',
};
$scope.$watchCollection('settings', function(newObj, oldObj) {
// Loop through new object and compare
angular.forEach(newObj, function(val, key) {
if(newObj[key] !== oldObj[key]) {
// settings[key] changed
}
});
}, true);
No, it's not possible - the only information you get is the new and previous value of an watched object.
You can tell that by looking at the Angular digest loop implementation:
// I ommited most of the code, leaving only the relevant part
if ((watchers = current.$$watchers)) {
while (/* iterate over watchers */) {
// Compare the current value with the last known value
if ((value = watch.get(current)) !== (last = watch.last)) {
// Notify the callback - only the whole values (new and last) are supplied.
watch.fn(value, ((last === initWatchVal) ? value : last), current);
}
}
You could manually enumerate object properties and compare them, or you could use some third party library for that. Quick NPM search returned this: deep-diff (it is available through bower as well). If you do opt to use it, it could look like:
$scope.$watch('watchedObject', function(newValue, oldValue) {
var differences = diff(newValue, oldValue);
// now inspect the differences array to see if there are any
});
Watching an object is not possible, unless you know the variable name in the $scope.
Say you have an object you want to watch at $scope.myobject. You will iterate over the object properties:
var objprops = []
angular.foreach(myobject, function(value, key) {
objprops.push("myobject." + key);
});
objexpr = "[" + objprops.join(",") + "]";
//objexpr will be something like "[myobject.a, myobject.b, myobject.c, ...]"
Then you watch over this array:
$scope.$watch(objexpr, function(newv, oldv){
//do iteration here
});
The big caveat here is that, to detect the changed values, you must iterate the newv array against the oldv array and remember objprops to know which field actually changed.
It's not clean... at all

Checking if a dynamic property exists in a javascript array

I have code that dynamically adds properties to an array.
data.tagAdded[tag.Name] = {
tag: tag,
count: 1,
};
Later in my code I need to check rather data.tagAdded has properties. If it doesn't have properties I need to do some other code. The problem is I can't figure out how to check for the existence properties.
The tagAdded = [] is always an array rather it contains properties or not, so I can't check if it is null. I can't say if property because I don't know the name of the property since it is dynamic. I can't check length because an array with properties is of length 0.
Any other way to check if properties exist?
Assuming you just want to see if you've assigned any key-value pairs to your associative array (just FYI, for what you're doing, an object might serve you better), you can do the following:
function isEmpty(o) {
return !Object.keys(o).length && !o.length;
}
var x = [];
isEmpty(x);
=> true
x['foo'] = 'bar';
isEmpty(x);
=> false
delete x.foo;
isEmpty(x);
=> true
x.push(1);
isEmpty(x);
=> false
You can try
for (var prop in tagAdded) {
if (tagAdded.hasOwnProperty(prop)) {
console.log("property exists");
}
}

How do you access an object within an object from an argument in Javascript? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Accessing nested JavaScript objects with string key
I have the function
function _get(name) {
return plugin._optionsObj[name] !== undefined ?
plugin._optionsObj[name] : plugin._defaults[name];
}
I would like to be able to have objects inside of my _defaults object, but then I don't know how to retrieve them but using just one set of square brackets.
i.e.
plugin._defaults = {
val1: 1,
val2: 2,
obj1: {
someVal: 3
}
}
Is it possible to access 'someVal' from the function I have above? I tried passing 'obj1.someVal' for the argument and it didn't work. Ideas?
Edit: I have found a solution and I posted it below as an answer. I've written a very nice little function to do go through the nested values with a string and I didn't have to change my function much to implement it. I hope this helps anyone in a similar situation.
I suspect that you won't always have a one-level nested object to access, so the cleaner way to do this is to use a function that traverses an object based on a string path. Here's one that is coded as a mixin for Underscore. You can then just use it like so:
_.deep(plugin._defaults, 'obj1.someVal');
This thread also has some non-Underscore alternatives.
Pass multiple arguments, and iterate over the arguments object.
function _get(/* name1, name2, namen */) {
var item = plugin._optionsObj,
defItem = plugin._defaults;
for (var i = 0; i < arguments.length; i++) {
item = item[arguments[i]];
defItem = defItem[arguments[i]];
if (item == null || defItem == null)
break;
}
return item == null ? defItem : item;
}
var opt = _get("obj1", "someVal")
I found a solution for this problem, at least one that will accommodate myself, and I'd like to share it in case it can help someone else with this problem. My biggest difficulty is that I did not know the depth of the nested value so I wanted to find a solution that would work for deeply nested objects and without requiring to redesign anything.
/* Retrieve the nested object value by using a string.
The string should be formatted by separating the properties with a period.
#param obj object to pass to the function
propertyStr string containing properties separated by periods
#return nested object value. Note: may also return an object */
function _nestedObjVal(obj, propertyStr) {
var properties = propertyStr.split('.');
if (properties.length > 1) {
var otherProperties = propertyStr.slice(properties[0].length+1); //separate the other properties
return _nestedObjVal(obj[properties[0]], otherProperties); //continue until there are no more periods in the string
} else {
return obj[propertyStr];
}
}
function _get(name) {
if (name.indexOf('.') !== -1) {
//name contains nested object
var userDefined = _nestedObjVal(plugin._optionsObj, name);
return userDefined !== undefined ? userDefined : _nestedObjVal(plugin._defaults, name);
} else {
return plugin._optionsObj[name] !== undefined ?
plugin._optionsObj[name] : plugin._defaults[name];
}
}
To retrieve objects inside of your _defaults object you'll need to improve your _get function.
For example you may pass an array of strings (each string representing a propery name) to _get to allow access to deeply nested objects.

Categories