I'm trying to figure out how to check if a deeply nested JSON object, with several unknown arrays and properties contains a property that I'm looking for. I'm looking for a property that is called "isInvalid". If the field is there and the value of that key is true. I want to return false.
var checkValidity = function (data) {
for (var property in data) {
if (data.hasOwnProperty(property)) {
if (property == "isInvalid" && data[property] === true) {
return false;
}
else {
if (typeof data[property] === "object" && data[property] !== null) {
this.checkValidity(data[property]);
}
}
}
}
};
This is the code I've been trying out but I'm unable to get that to work. I have been looking into underscore also, but cant find the needed functions. Anyone has an idea? (No reg exp please)
If you really just want to check for property presence regardless of its particular location within JSON, then the easiest/fastest way is substring search in the source JSON string. If the latter is well-formed, then the property should be encoded in JSON as '"isInvalid":true'.
var checkValidity = function (jsonstr) {
return jsonstr.indexOf('"isInvalid":true') >= 0;
}
You can check like this
var s = {a:'1',b:'2'};
if(Object.getOwnPropertyNames(s).indexOf('a') != -1){
console.log('available');
}else{
console.log('Not available');
};
editing answer... UPDATE
var s = {
a1: '1',
b: '2',
c: {
a: '11'
}
};
var checkValidity = function (data) {
if (Object.getOwnPropertyNames(data).indexOf('a') != - 1) {
console.log('Found that key!!!');
} else {
for (var property in data) {
if (Object.getOwnPropertyNames(property).indexOf('a') != - 1) {
console.log('Found that key!!!');
} else {
if (typeof data[property] === 'object' && data[property] !== null) {
console.log('not found continue in inner obj..');
this.checkValidity(data[property]);
}
}
}
};
};
checkValidity(s);
It tests for every nesting level the property isInvalid and if not, all other properties as object and their content. Array#every breaks if one return is false.
function checkValidity(data) {
return !data.isInvalid && Object.keys(data).every(function (property) {
if (typeof data[property] === "object" && data[property] !== null) {
return checkValidity(data[property]);
}
return true;
});
}
var data = {
a: 1,
b: 2,
c: {
isInvalid: true,
a: false
}
};
document.write('checkValidity() should be false: ' + checkValidity(data) + '<br>');
data.c.isInvalid = false;
document.write('checkValidity() should be true: ' + checkValidity(data));
For complex json searching like this, I would use jsonpath ( http://goessner.net/articles/JsonPath/ ) which is the JSON equivalent of xpath.
To find the isInvalid field no matter where it is in the json, you would use it like this:
jsonPath(data, "$..isInvalid")
Related
I'm building an utility function that should search for a property name and return its value once it is found. It should do this recursively:
// Function
util.findVal = (object, propName) => {
for (let key in object) {
if (key === propName) {
console.log(propName)
console.log(object[key])
return object[key]
} else {
util.findVal(object[key], propName)
}
}
}
// Input
object: {
photo: {
progress: 20
}
}
// Usage
util.findVal(object, 'progress')
However the console log goes forever and the browser crashes. What am I doing wrong?
EDIT:
This is how I'm calling the function:
// Input
item: {
photo: {
file: {},
progress: 20
}
}
this.findProgress(item)
methods: {
findProgress (item) {
return util.findVal(item, this.propName)
}
}
You could use Object.keys and iterate with Array#some.
function findVal(object, key) {
var value;
Object.keys(object).some(function(k) {
if (k === key) {
value = object[k];
return true;
}
if (object[k] && typeof object[k] === 'object') {
value = findVal(object[k], key);
return value !== undefined;
}
});
return value;
}
var object = { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
Your code has a few errors:
You're recursively calling util.findVal but not returning the result of the call. Code should be return util.findVal(...)
You're not passing the attribute name key to the recursive call
You're not handling the possibility of a reference loop
If an object contains a key and also a sub-object that contains the key which value is returned is random (depends on the sequence in which the keys are analyzed)
The third problem is what can cause infinite recursion, for example:
var obj1 = {}, obj2 = {};
obj1.x = obj2; obj2.y = obj1;
if you just keep looking recursively searching in obj1 or obj2 could lead to infinite recursion.
Unfortunately for reasons not clear to me in Javascript is impossible to know the object "identity"... (what Python id(x) does) you can only compare an object to another. This means that to know if an object has already been seen in the past you need a linear scan with known objects.
ES6 added the possibility to check object identity with Set and Map where objects can be used as keys. This allows for faster (sub-linear) search times.
A search solution that runs in depth order could be for example:
function findVal(obj, key) {
var seen = new Set, active = [obj];
while (active.length) {
var new_active = [], found = [];
for (var i=0; i<active.length; i++) {
Object.keys(active[i]).forEach(function(k){
var x = active[i][k];
if (k === key) {
found.push(x);
} else if (x && typeof x === "object" &&
!seen.has(x)) {
seen.add(x);
new_active.push(x);
}
});
}
if (found.length) return found;
active = new_active;
}
return null;
}
given an object and an attribute name, returns all the values found with that name at the first depth they are found (there can be more than one value: for example when searching {x:{z:1}, y:{z:2}} for the key "z" two values are at the same depth).
The function also correctly handles self-referencing structures avoiding infinite search.
Don't write your own utility if you can avoid it.
Use something like jsonpath
Some examples of supported syntax:
JSONPath Description
$.store.book[*].author The authors of all books in the store
$..author All authors
$.store.* All things in store, which are some books and a red bicycle
$.store..price The price of everything in the store
$..book[2] The third book
$..book[(#.length-1)] The last book via script subscript
$..book[-1:] The last book via slice
$..book[0,1] The first two books via subscript union
$..book[:2] The first two books via subscript array slice
$..book[?(#.isbn)] Filter all books with isbn number
try changing else statement like this
return util.findVal(object[key],propName)
I know this is an old post, but I found it helpful to answer a problem I had with recursively finding a value by it's key. I further developed the answer given by Nina Scholz, and came up with the following. It should be quicker as it is not creating an array of all of the keys each time it is recursively invoked. Also, this will explicitly return false if the key is not found.
function findVal(obj, keyToFind) {
if (obj[keyToFind]) return obj[keyToFind];
for (let key in obj) {
if (typeof obj[key] === 'object') {
const value = findVal(obj[key], keyToFind);
if (value) return value;
}
}
return false;
}
var object = { photo: { progress: 20 }};
console.log(findVal(object, 'progress'));
I think you are saying that you want to look for the property name anywhere recursively within the objects tree of properties and sub-properties. If so, here is how I would approach this:
var object1 = _getInstance(); // somehow we get an object
var pname = 'PropNameA';
var findPropertyAnywhere = function (obj, name) {
var value = obj[name];
if (typeof value != 'undefined') {
return value;
}
foreach(var key in obj) {
var v2 = findPropertyAnywhere(obj[key], name);
if (typeof v2 != 'undefined') {
return v2;
}
}
return null;
}
findPropertyAnywhere(object1, pname);
Think about it if there is no key found.
I think you could do something like this instead of search
return object[propName] || null
In your code there was a breakpoint missing, I guess you are trying to search inside the whole object not just the directly related attributes so here is an edit for you code
EDIT:
util.findVal = (object, propName) =>{
if(!!object[propName]){
return object[propName]
}else{
for (let key in object) {
if(typeof object[key]=="object"){
return util.findVal(object[key], propName)
}else{
return null
}
}
}
}
Found this question in the realm of needing a general solution to check if an object contains a specific value anywhere in its hierarchy (regardless of the key), which can include arrays of course. So the following does not answer OPs question directly or improve upon other solutions but it might help others looking for the same thing I did and finding this post:
function hasValue(object, value) {
return Object.values(object).some(function(val) {
if (val === value) {
return true;
}
if (val && typeof val === 'object') {
return hasValue(val, value);
}
if (val && val.isArray()) {
return val.some((obj) => {
return hasValue(obj, value);
})
}
});
}
it is of course inspired by #Nina Scholz 's solution!
An answer depends a on how complex you want to get. For example a JSON parsed array doesn't contain functions - and I'm fairly certain it won't contain property value set to a parent node in object tree.
This version returns the property value of the first property name found whilst searching the object tree. undefined is returned if either the named property was not found or has a value of undefined. Some modifications would be needed to tell the difference. It does not re-search parent nodes already being searched, nor try to scan null objects!
let util = {};
util.findVal = (object, propName, searched=[]) => {
searched.push( object)
for (let key in object) {
if (key === propName) {
return object[key]
}
else {
let obj = object[ key]
if( obj && (typeof obj == "object" || typeof obj == "function")) {
if( searched.indexOf(obj) >=0) {
continue
}
let found = util.findVal(obj, propName, searched)
if( found != searched) {
return found
}
}
}
}
searched.pop();
// not in object:
return searched.length ? searched : undefined
}
I ended up writing this function.
It is a refactor of a function found here: Recursively looping through an object to build a property list
added a depth parameter to avoid stack overflow in chrome devtools.
function iterate(obj, context, search, depth) {
for (var property in obj) {
if (Object.prototype.hasOwnProperty.call(obj, property)) {
if(typeof obj[property] == 'function') continue;
if( property == search ){
console.log(context+property);
return;
}
if (typeof obj[property] == "object" && depth < 7) {
//console.log('--- going in: ' + context+property);
iterate(obj[property], context+property+'.', search, depth+1);
}
/*else {
console.log(context+property);
}*/
}
}
}
Returns the value of the field with the specified name.
data is the root node/object.
keyName is a string name of the field/member.
If keyName specifies a field that is itself an object, then that object is returned.
function find (data, keyName) {
for (const key in data) {
const entry = data[key]
if (key === keyName)
return entry
if (typeof entry === 'object') {
const found = find(entry, keyName)
if (found)
return found
}
}
}
The for loop goes through each field and if that field is an object then it will recurse into that object.
Here is a piece of code which find the key you are looking for in your rootObj tree. And add it to the root object. So by the end you will have access to you key like this rootObj[key].
findKeyVal(object, key, rootObj) {
if(object instanceof Object) {
let keys = Object.keys(object);
if(keys.includes(key) && !isNullOrUndefined(object[key])) {
rootObj[key] = object[key];
return;
}
else {
keys.filter(k => object[k] instanceof Object).forEach( k => {
this.findKeyVal(object[k], key, rootObj);
})
}
}
}
Old question, but to check if the property exists anywhere in the hierarchy of an object, try this simple option
var obj = {
firstOperand: {
firstOperand: {
firstOperand: {
sweptArea: 5
}
}
}
};
function doesPropertyExists ( inputObj, prop )
{
return JSON.stringify(obj).indexOf( "\""+ prop +"\":" ) != -1;
};
console.log( doesPropertyExists( obj, "sweptArea" ) );
console.log( doesPropertyExists( obj, "firstOperand" ) );
console.log( doesPropertyExists( obj, "firstOperand22" ) );
I have an original object that is modified after an API call. I need to make sure that any fields that were originally not empty are reassigned to their original value. For example if articleTitle was initially filled out, and then after the API call it gets replaced with an empty value, I want to reassign it back to the original articleTitle value from the old object.
The two objects have the same keys, but I can't assume that the data coming back from the response is always going to be valid (but the original object always has valid data, that's why I need to reassign any empty fields to original values).
I (kinda) have a theoretically functional method, however I'm wondering if there is a more efficient way to do this. Here's what I have:
function evaluateEmptyValues = (originalReference, reference) {
// Get keys of both reference objects
var newReference = Object.entries(reference);
var oldReference = Object.entries(originalReference);
// Get length of both reference objects
var newReferenceLength = newReference.length;
var oldReferenceLength = oldReference.length;
// Double check objects are of the same length -- they always should be
if (newReferenceLength == oldReferenceLength) {
// Cycle through both objects
for (var i = 0; i < newReference.length; i++) {
console.log('i is ' + i);
// Again, these two lengths should be equal
if (newReference[i].length == oldReference[i].length) {
// Check if elements in current iteration is an object --
// if one is an object, then the other SHOULD also be
if ((typeof(newReference[i][j]) == 'object' &&
typeof(oldReference[i][j]) == 'object'
) {
// If both are objects, repeat lines 3 and 4
var currentNewReference = Object.entries(newReference[i][j]);
var currentOldReference = Object.entries(oldReference[i][j]);
// Get their lengths
var currentNewReferenceLength = currentNewReference.length;
var currentOldReferenceLength = currentOldReference.length;
// Both should be of the same length
if (currentNewReferenceLength == currentOldReferenceLength) {
for (var io = 0; io < currentNewReferenceLength.length; io++) {
console.log('io is ' + io);
// Both should also be of the same length
if (currentNewReference[io].length == currentOldReference[io].length) {
// For each iteration...
for (var jo = 0; jo < currentNewReference[io].length; jo++) {
// Check for empty values
if (currentNewReference[io][jo] == undefined ||
currentNewReference[io][jo] == null ||
(typeof(currentNewReference[io][jo]) == 'string' && currentNewReference[io][jo].trim() == '')
) {
// If empty, then reassign the empty value in the new reference
// object with the value of the field from the old reference
// object, regardless of whether or not the old value is also empty/null
currentNewReference[io][jo] = currentOldReference[io][jo];
}
}
} else {
// Serious problem
}
}
} else {
// Serious problem
}
} else {
// Cycle through current field
for (var j = 0; j < newReference[i].length; j++) {
// Check for nulls or empties
if (newReference[i][j] == undefined ||
newReference[i][j] == null ||
(typeof(newReference[i][j]) == 'string' && newReference[i][j].trim() == '')
) {
// Assign old value to new value, regardless of
// whether or not old value is also empty
newReference[i][j] = oldReference[i][j];
}
}
}
} else {
// Serious problem
}
}
} else {
// Serious problem
}
I doubt this is a very scalable or maintainable approach, and I'm wondering if there are any suggestions on enhancing this function, preferably using ES5, unless the ES6+ version works in most browsers.
For some reference, here are the two objects:
Here, articleTitle is empty.
Here, it is filled out from the API call. This is expected and needed, however imagine if it was the other way around, and articleTitle came back empty in the newReference after the API call
Edit:
Using the accepted answer plus an adjustment, this solved my specific problem:
function evaluateEmptyValues(reference, originalReference) {
var vm = this;
// Get keys and values of both reference objects
referenceLength = Object.entries(reference).length;
originalReferenceLength = Object.entries(originalReference).length;
if (referenceLength == originalReferenceLength) {
try {
// Cycle through both objects
for (var prop in reference) {
if (reference[prop] != undefined || reference[prop] != null) {
if (typeof (reference[prop]) == 'string' && reference[prop].trim() != '') {
// If both current elements are objects, recurse
if (typeof reference[prop] == 'object' && typeof originalReference[prop] == 'object') {
vm.evaluateEmptyValues(reference[prop], originalReference[prop])
}
// If both current elements are arrays, recurse
if (Array.isArray(reference[prop]) && typeof Array.isArray(originalReference[prop])) {
reference[prop].forEach((item, index) => vm.evaluateEmptyValues(item, originalReference[prop][index]));
}
// If new value is null, empty or undefined, assign it to old value,
// regardless of whether or not the old value was also null/empty.
//
///// This is to ensure that no existing previous values are
///// overwritten with any nulls or empty values
} else {
reference[prop] = originalReference[prop];
}
} else {
reference[prop] = originalReference[prop];
}
}
} catch(err) {
console.log(err);
}
}
console.log(reference);
You can simplify your function by a lot using recursion and a for ... in loop. I made two test objects to illustrate all the cases of your original example. In case it hits an array of objects it will iterate through that array and check for empty values recursively as well. Please see snippet below:
function evaluateEmptyValues(reference, originalReference) {
if (reference.length == originalReference.length) {
for (var prop in reference) {
if (typeof reference[prop] == 'object' && typeof originalReference[prop] == 'object') {
evaluateEmptyValues(reference[prop], originalReference[prop])
}
if (Array.isArray(reference[prop]) && typeof Array.isArray(originalReference[prop])) {
reference[prop].forEach((item, index) => evaluateEmptyValues(item, originalReference[prop][index]));
}
if (reference[prop] == undefined || reference[prop] == null ||
(typeof (reference[prop]) == 'string' && reference[prop].trim() == '')) {
reference[prop] = originalReference[prop];
}
}
}
}
const original = {
name: "Jack",
employee: {
firstName: "Nathan",
favoriteAnimal: {
species: "Donkey",
nature: "Lazy"
},
favoriteBeverages: [
{ name: "Beer", temperature: "Cold" },
{ name: "More beer", temperature: "Colder" }
]
},
occupation: "Plumber"
}
const newObject = {
name: "Jack",
employee: {
firstName: " ",
favoriteAnimal: {
species: null,
nature: "Lazy"
},
favoriteBeverages: [
{ name: "Beer", temperature: ""},
{ name: null, temperature: "Colder" }
]
},
occupation: undefined
}
evaluateEmptyValues(newObject, original);
console.log(newObject);
I think instead of using lot's of if conditions, you can try lodash, and use isEqual method which do a deep comparison between two values ( in your case two objects ), your code can be much cleaner as well.
var object = { 'a': 1 };
var other = { 'a': 1 };
_.isEqual(object, other);
// => true
You could make use of a recursive function. Something like this.
function mapper(oldObj, newObj) {
Object.entries(oldObj).forEach(([key, value]) => {
if (!newObj[key]) {
newObj[key] = value;
} else if (Array.isArray(newObj[key])) {
newObj[key].forEach((o, i) => mapper(oldObj[key][i], o));
} else if (Object.prototype.toString.call(newObj[key]) === "[object Object]") {
mapper(oldObj[key], newObj[key]);
}
});
return newObj;
}
const next = mapper(oldObj, newObj);
This will basically loop over all the items in the original object, and set the key/value in the new object if it doesn't exist.
Let's say we have this JavaScript object:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
How can we check if value property exists?
I can see only two ways:
First one:
if(object && object.innerObject && object.innerObject.deepObject && object.innerObject.deepObject.value) {
console.log('We found it!');
}
Second one:
if(object.hasOwnProperty('innerObject') && object.innerObject.hasOwnProperty('deepObject') && object.innerObject.deepObject.hasOwnProperty('value')) {
console.log('We found it too!');
}
But is there a way to do a deep check? Let's say, something like:
object['innerObject.deepObject.value']
or
object.hasOwnProperty('innerObject.deepObject.value')
There isn't a built-in way for this kind of check, but you can implement it easily. Create a function, pass a string representing the property path, split the path by ., and iterate over this path:
Object.prototype.hasOwnNestedProperty = function(propertyPath) {
if (!propertyPath)
return false;
var properties = propertyPath.split('.');
var obj = this;
for (var i = 0; i < properties.length; i++) {
var prop = properties[i];
if (!obj || !obj.hasOwnProperty(prop)) {
return false;
} else {
obj = obj[prop];
}
}
return true;
};
// Usage:
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
}
console.log(obj.hasOwnNestedProperty('innerObject.deepObject.value'));
You could make a recursive method to do this.
The method would iterate (recursively) on all 'object' properties of the object you pass in and return true as soon as it finds one that contains the property you pass in. If no object contains such property, it returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
function hasOwnDeepProperty(obj, prop) {
if (typeof obj === 'object' && obj !== null) { // only performs property checks on objects (taking care of the corner case for null as well)
if (obj.hasOwnProperty(prop)) { // if this object already contains the property, we are done
return true;
}
for (var p in obj) { // otherwise iterate on all the properties of this object.
if (obj.hasOwnProperty(p) && // and as soon as you find the property you are looking for, return true
hasOwnDeepProperty(obj[p], prop)) {
return true;
}
}
}
return false;
}
console.log(hasOwnDeepProperty(obj, 'value')); // true
console.log(hasOwnDeepProperty(obj, 'another')); // false
Alternative recursive function:
Loops over all object keys. For any key it checks if it is an object, and if so, calls itself recursively.
Otherwise, it returns an array with true, false, false for any key with the name propName.
The .reduce then rolls up the array through an or statement.
function deepCheck(obj,propName) {
if obj.hasOwnProperty(propName) { // Performance improvement (thanks to #nem's solution)
return true;
}
return Object.keys(obj) // Turns keys of object into array of strings
.map(prop => { // Loop over the array
if (typeof obj[prop] == 'object') { // If property is object,
return deepCheck(obj[prop],propName); // call recursively
} else {
return (prop == propName); // Return true or false
}
}) // The result is an array like [false, false, true, false]
.reduce(function(previousValue, currentValue, index, array) {
return previousValue || currentValue;
} // Do an 'or', or comparison of everything in the array.
// It returns true if at least one value is true.
)
}
deepCheck(object,'value'); // === true
PS: nem035's answer showed how it could be more performant: his solution breaks off at the first found 'value.'
My approach would be using try/catch blocks. Because I don't like to pass deep property paths in strings. I'm a lazy guy who likes autocompletion :)
JavaScript objects are evaluated on runtime. So if you return your object statement in a callback function, that statement is not going to be evaluated until callback function is invoked.
So this function just wraps the callback function inside a try catch statement. If it catches the exception returns false.
var obj = {
innerObject: {
deepObject: {
value: 'Here am I'
}
}
};
const validate = (cb) => {
try {
return cb();
} catch (e) {
return false;
}
}
if (validate(() => obj.innerObject.deepObject.value)) {
// Is going to work
}
if (validate(() => obj.x.y.z)) {
// Is not going to work
}
When it comes to performance, it's hard to say which approach is better.
On my tests if the object properties exist and the statement is successful I noticed using try/catch can be 2x 3x times faster than splitting string to keys and checking if keys exist in the object.
But if the property doesn't exist at some point, prototype approach returns the result almost 7x times faster.
See the test yourself: https://jsfiddle.net/yatki/382qoy13/2/
You can also check the library I wrote here: https://github.com/yatki/try-to-validate
I use try-catch:
var object = {
innerObject:{
deepObject:{
value:'Here am I'
}
}
};
var object2 = {
a: 10
}
let exist = false, exist2 = false;
try {
exist = !!object.innerObject.deepObject.value
exist2 = !!object2.innerObject.deepObject.value
}
catch(e) {
}
console.log(exist);
console.log(exist2);
Try this nice and easy solution:
public hasOwnDeepProperty(obj, path)
{
for (var i = 0, path = path.split('.'), len = path.length; i < len; i++)
{
obj = obj[path[i]];
if (!obj) return false;
};
return true;
}
In case you are writing JavaScript for Node.js, then there is an assert module with a 'deepEqual' method:
const assert = require('assert');
assert.deepEqual(testedObject, {
innerObject:{
deepObject:{
value:'Here am I'
}
}
});
I have created a very simple function for this using the recursive and happy flow coding strategy. It is also nice to add it to the Object.prototype (with enumerate:false!!) in order to have it available for all objects.
function objectHasOwnNestedProperty(obj, keys)
{
if (!obj || typeof obj !== 'object')
{
return false;
}
if(typeof keys === 'string')
{
keys = keys.split('.');
}
if(!Array.isArray(keys))
{
return false;
}
if(keys.length == 0)
{
return Object.keys(obj).length > 0;
}
var first_key = keys.shift();
if(!obj.hasOwnProperty(first_key))
{
return false;
}
if(keys.length == 0)
{
return true;
}
return objectHasOwnNestedProperty(obj[first_key],keys);
}
Object.defineProperty(Object.prototype, 'hasOwnNestedProperty',
{
value: function () { return objectHasOwnNestedProperty(this, ...arguments); },
enumerable: false
});
I want to search two item (name=string and location=json). this search is (one input box and two columns for search).
at the moment with this code I can find 'name' but i need I need to find location also.
if(textToCheck !== '') {
if((searchArray[i]['location']).toLowerCase().search(textToCheck) === -1) {
display = false;
}
}
the code that I suggest and doesn't work is:
if(textToCheck !== '') {
if((searchArray[i]['name']).toLowerCase().search(textToCheck) === -1 || (searchArray[i]['location']).toLowerCase().search(textToCheck) === -1) {
display = false;
}
}
error is :
Uncaught TypeError: Object 123 Street,xxx,xx,Canada,123rd Street,xxx,xx,123 xxx,12345 xxx,France has no method 'toLowerCase' FilterController.showFilteredSet (anonymous function)
As you said location=json, actually searchArray[i]['location'] is a object but not string. You need to do search depend on the what the object like.
Or simply change the object to string format like below:
JSON.stringify(searchArray[i]['location']).toLowerCase().search(textToCheck) === -1
JSON.stringify() is fine. But that searches in the object keys also.
This means:
if your "JSON" object looks like this:
({
street: 'my street',
country: 'Texas'
})
JSON.stringify(obj).toLowerCase().search('country') will find a result, even if the "data" doesn't contain it.
instead:
use a generalized way to do a flat search on objects.
Object.prototype.search = function(subject) {
for(var k in this) {
if(this.hasOwnProperty(k) && this[k].toString().toLowerCase().search(subject) !== -1)
return true;
}
return false;
};
var myObj = ({ foo: 'bar', hello: 'world' });
console.log(myObj.search('ar')); //search for "ar", returns true
console.log(myObj.search('ponyo!')); //search for "ponyo!", returns false
console.log(myObj.search('hello')); //search for "hello", returns false
in your case that would decline to:
//somewhere above, run this only once:
Object.prototype.search = function(subject) {
for(var k in this) {
if(this[k].toString().toLowerCase().search(subject) !== -1)
return true;
}
return false;
};
/////
if(textToCheck !== '') {
if((searchArray[i]['name']).toLowerCase().search(textToCheck) === -1 &&
(searchArray[i]['location']).search(textToCheck) === false) {
display = false;
}
}
please be warned that this code modifies the Object prototype, adding a "search" function to all objects (this might conflict with other libraries, you may or may not be using, that want to do the same).
I am in the following situation:
I have to check if the object parameter passed to a function is valid:
Exmaple:
function (opt) {
if (opt && opt.key && opt.key2) {
// make something
}
}
Is there a better way to make this kind of check?
Not really.
Unless you can use opt.pleaseReadMyMind() ;-)
You could create a method that will check if all fields have values different to null, though.
That's the most compact way of doing it.
The most correct way would be:
if( typeof opt !== "undefined" && typeof opt.key !== "undefined" && typeof opt.key2 !== "undefined") {
But as you can see that's quite a mouthful and not really necessary.
Just write a simple test routine to verify the object given a list of property names:
// usage: testProps(object to test, [list, of, property, names])
// returns true if object contains all properties
function testProps(obj, props)
{
if (obj === null)
return false;
var i;
for (i=0; i<props.length; ++i)
{
if (!(props[i] in obj))
return false;
}
return true;
}
And then in your function:
if (!testProps(obj, ['key', 'key2'])
return;
What you are doing is valid, but it does have flaws.
if (opt && opt.key && opt.key2) {
This check would fail if opt.key has falsely values [0,null,false,and so on]
In that case you would have to do a typeof check to make sure it is not undefined.
if (opt && typeof opt.key !== "undefined" && opt.key2) {
Yeah, but it's only "better" if you have a lot of keys to check, not just three. Something like this:
function opt(opt) {
for(var i = 0; i<3; i++) {
if(typeof opt["key"+((i > 0) ? "" : i + 1))] === "undefined") {
return;
}
}
// create object
}
If opt is undefined all its keys will be too, so there's an implicit check for that as well.
You could also define the variable names you want to check in array, something like this:
var propsToCheck = ["key", "key1", "key2"];
function(opt) {
for(var i = 0, ii = propsToCheck.length; i<ii; i++) {
if(typeof opt[propsToCheck[i]] === "undefined") {
return;
}
// create object
}
}
Not really much of a better solution, but it does allow for less typing if you're planning on checking more than three or four properties.
You could always do it like this:
function validate(o, args) {
if (typeof(o) == 'object' && args instanceof Array) {
for (var i = args.length - 1; i >= 0; --i) {
if (typeof(o[args[i]]) === 'undefined') return false;
}
return true;
} else {
return false;
}
}
function myFunction(obj) {
if (validate(obj, ['foo', 'bar'])) {
// Your code goes here.
} else {
// Object passed to the function did not validate.
}
}
Here's a fiddle for you: http://jsfiddle.net/reL2g/