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' });
Related
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));
I basicly need to do some replace logic to change the '.' to a '>' at here everything fine, i can just use the replace method from javascript, but i am searching the best way to do it.
Basicly i will use this function just for 1 specific task nothing more, i want to do this replace logic to my Name propertiy and description inside the object, so instead of doing a simple method that does the replace i need to pass it two times down.
At the moment i have this repeated: element.Name.replace('.', ' > ')
i created a method, but i thaught as the best possible way to maybe pass it to the function like: replaceMethod(firstProp,secondProp) where each prop gets replaced, so how can i inside the replace method just apply the same logic to all my arguments without using a useless for loop?
something like this:
replaceMethod(firstProp,secondProp) {
allArgs.replace('.', ' > ')
}
i did this:
callerFunc() {
// service get the object material, it has a name and description with '.'
replaceMethod(material,material.Name,material.Description)
// do some logic after the method with the material
}
replaceMethod(material,...keys) {
keys.forEach(k => material[k] = material[k].replace(/\./g, ' > '));
}
In ES6, you could use rest parameters ... for collecting all arguments.
function replaceMethod(...keys) {
keys.forEach(k => object[k] = object[k].replace(/\./g, ' > '));
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
ES5 with use of arguments object.
function replaceMethod() {
Array.prototype.forEach.call(arguments, function (k) {
object[k] = object[k].replace(/\./g, ' > ');
});
}
var object = { name: 'foo.bar.baz', town: 'st.peter' };
replaceMethod('name', 'town');
console.log(object);
I would recommend passing the object as a parameter with the keys of the properties you want to change. Then return a new object with the changes instead of changing the object in place. This is a more functional approach without side effects. You can use the array reduce method. It is most convenient in ES6 using the spread operator ...
function replaceForKeys(obj, ...keys) {
return keys.reduce(
function (result, k) {
return { ...result, [k]: obj[k].replace(/\./g, ' > ') };
},
Object.assign({}, obj)
);
}
var obj = { 'foo': 'foo.bar', 'bar': 'bar.foo' };
var replaced = replaceForKeys(obj, 'foo', 'bar');
So the function takes every argument after the object as an array of keys and reduces over them returning the original object with the property replaced each time. The reduce method takes an initial value as the second parameter and in this case we use Object.assign to use a copy of the original object as the initial value. the [k]: syntax in the object is new in ES6 I believe and is a computed key. It just lets you assign keys in the object without knowing their value beforehand.
Here is some JSON:
{
"environments":{
"production":{
"zmq_config":{
"host":"*",
"port":"7676"
},
"redis_server_config":{
"host":"localhost",
"port":"26379"
}
},
"dev_remote":{
"zmq_config":{
"host":"*",
"port":"5555"
},
"redis_server_config":{
"host":"localhost",
"port":"16379"
}
},
"dev_local":{
"zmq_config":{
"host":"*",
"port":"5555"
},
"redis_server_config":{
"host":"localhost",
"port":"6379"
}
}
}
}
I want to create a test in my test suite that ensures all of the properties have the same properties of their complements.
For example, for each property of "environments", I want to check that they have the same properties; in this case they do - they all have 2 properties "zmq_config" and "redis_server_config". Now I want to do at least one more level of checking. For properties "zmq_config" and "redis_server_config", I want to check that they in turn have the same properties "host" and "port".
You get the idea.
Is there a library that can do this? Is there some sort of JavaScript identity operator that check for this, just looking at the top level objects?
Now the easiest way I can think of doing this is simply to iterate through and look at each property with the same name (making the assumption that properties with the same name are in the same place in the object hierarchy), and then simply seeing if they have the same subproperties.
Is Underscore.js the best option? It seems Underscore has this functionality which might work:
_.isEqual(obj1, obj2);
from my research it looks like this is the best candidate:
_.isMatch(obj1,obj2);
For each object to test, you can use Object.keys function to extract the keys of the object and then compare them, because you only want to know if properties are equals, the value not matters.
Then, when you extract the keys of each object, you can compare using _.isEqual function by provided by lodash instead of underscore (usually lodash has better performance).
To automate as possible, you should create a recursive function to extract the keys and compare them.
Hacked this real quick but it should do you justice. It returns true if all nested object keys match. At each level it checks if the array of keys matches the other object's array of keys and it does that recursively.
function keysMatch(data1, data2) {
var result = null;
function check(d1, d2) {
if (result === false) {
return false;
}
if (_.isObject(d1) && _.isObject(d2)) {
if (allArraysAlike([_.keys(d1), _.keys(d2)])) {
result = true;
_.forOwn(d1, function (val, key) {
check(d1[key], d2[key]);
});
} else {
result = false;
}
}
return result;
}
return check(data1, data2);
}
function allArraysAlike(arrays) {
return _.all(arrays, function (array) {
return array.length == arrays[0].length && _.difference(array, arrays[0]).length == 0;
});
}
console.log(keysMatch(json1, json2));
http://jsfiddle.net/baafbjo8/2/
If you want a simple true/false answer, then a simple function can be created from basic javascript.
The function below uses ES5 features, but wouldn't be much more code using plain loops (and run a bit fast to boot, not that it's slow).
/**
* #param {Object} obj - Object to check properties of
* #param {Array} props - Array of properties to check
* #returns {boolean}
**/
function checkProps(obj, props) {
// List of members of obj
var memberNames = Object.keys(obj);
// Use keys of first object as base set
var baseKeys = Object.keys(obj[memberNames[0]]);
// Check every object in obj has base set of properties
// And each sub-object has props properties
return memberNames.every(function (memberName) {
// Get member
var member = obj[memberName];
// Get keys of this member
var memberKeys = Object.keys(member);
// First check that member has same keys as base, then that each sub-member
// has required properties
return memberKeys.length == baseKeys.length &&
baseKeys.every(function(key) {
return member.hasOwnProperty(key) &&
// Check sub-member properties
props.every(function(prop) {
return member[key].hasOwnProperty(prop);
});
});
});
}
console.log(checkProps(env,['host','port']));
For EcmaScript ed 4 compatability, requires polyfills for Array.prototype.every and Object.keys.
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/
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.