Checking if object is already in array not working - javascript

I am trying to check if an object is already in an array, following this answer here: How to determine if object is in array
I adjusted the function to suit my needs, and now it looks like this:
var _createDatesArray, _objInArray;
_objInArray = function(array, obj) {
var i;
i = 0;
while (i < array.length) {
console.log("array[i] == obj is ", array[i] === obj, " array[i] is ", array[i], " and obj is ", obj);
if (array[i] === obj) {
return true;
}
i++;
}
};
_createDatesArray = function(val) {
var result;
if (val != null) {
result = {
text: val
};
if (!_objInArray(scope.datesQuestion.dates, result)) {
scope.datesQuestion.dates.push(result);
}
return console.log(scope.datesQuestion.dates);
}
};
What I need to do, is basically see if the object is already in the array, and if is,t return true.
When debugging, the result of the console log is the following:
array[i] == obj is false array[i] is {text: "10/08/17"} and obj is
{text: "10/08/17"}
and the function says they are different (array[i] == obj is false) but they look the same to me.
I also checked the type of both, which is this:
typeof array[i] is "object"
typeof obj is "object"
can you help me with this? Why are they different? what can be different?
_createDatesArray is called when $scope of my angular app changes its value based on a ng-model, but I don't think this is relevant

They're two different objects with the same content. Comparing them with == or === will yield false.
Since you're using AngularJS, you can use angular.equals() instead to perform a deep comparison of the object's properties.

The objects you are comparing don't have the same reference, so == is returning false. See Object comparison in JavaScript for a more detailed explanation.
In this particular case, you could simply compare the text of dates to see if they are equivilant. However this wouldn't work for all objects like the function name suggests.
if (arr[i].text === obj.text)
Alternatively, you could create a method specific for checking if your array includes a given date and simplify it greatly using Array.prototype.some:
dateInArray = function (array, date) {
return array.some(function (arrayDate) {
return arrayDate.text === date.text
})
}
Or, more succinctly using ES6 arrow functions:
dateInArray = (array, date) => array.some(arrayDate => arrayDate.text === date.text)

array[i] === obj will return true ONLY if its the same object. In the link that you have referred the object being checked is the same object that is inserted in the array, thats why it returns true. In your case, you are creating a new object 'result' and adding the value in there. So the array does not contain the exact same object and hence returns false.
If 'text' is the only property in the object, instead of checking for the entire object you could check if the 'text' property in both the objects is same.
_objInArray = function(array, obj) {
var i;
i = 0;
while (i < array.length) {
if (array[i].text === obj.text) {
return true;
}
i++;
}
};

This happens because objects in JS are compared by reference, but not by values they have. But you need to compare objects by their value. So you need to get some third-party function or to write your own. One more option is to use angular built-in equals function.
angular.equals($scope.user1, $scope.user2);
For a better understanding you can read a good article on this subject here.

Related

Find whether Value in Object exists within an array

I am trying to find whether the value roomTypeFilter exists within an Object inside an array. I then want to perform conditional statements depending whether the value roomTypeFilter exists or not.
Below is my code
function includes(k) {
for (var i = 0; i < this.length; i++) {
if (this[i] === k || (this[i] !== this[i] && k !== k)) {
return true;
}
}
return false;
}
var dayValue = this.ui.dayConstraintList.val();
var arr = [courseTemplate.get('dayConstraints')[dayValue]];
console.log(arr);
arr.includes = includes;
console.log(arr.includes('roomTypeFilter'));
The first console.log returns an Object inside an array.
The second console.log returns false, in this case as roomTypeFilter exists inside the Object I want to return 'true' but I am unsure how to do so, any help would be greatly appreciated.
Instead of using includes, use hasOwnProperty. Take a look here for more information on the hasOwnProperty. It's pretty self-explanatory from its name - it essentially returns a boolean on whether or not the object has a property. I.e., in your case, you would use:
arr[0].hasOwnProperty('roomTypeFilter');
You can use hasOwnProperty method to check if object contains roomTypeFilter property.
...
if (this[i].hasOwnProperty(k)) {
...
}
...
You could refactor your includes function to use
array.prototype.some
some() executes the callback function once for each element present in the array until it finds one where callback returns a truthy value... If such an element is found, some() immediately returns true.
Here is an example.
var arr = [
{
roomTypeFilter: {
name: 'foo'
}
},
["roomTypeFilter", "foo"],
"roomTypeFilter foo"
]
function includes(arr, k) {
return arr.some(function(item) {
return item === Object(item) && item.hasOwnProperty(k);
})
}
console.log("Does arr includes roomTypeFilter ? - ", includes(arr, 'roomTypeFilter'))

How to rewrite the following function so it doesn't use for loops?

The following function takes an object, loops through each value and returns false if the object or its children have an empty or undefined property web. Otherwise, it returns true:
hasNoCategories (object) {
for (let key in object) {
const value = object[key]
for (let i = 0; i < value.length; i++) {
const item = value[i]
if (item.web !== undefined && item.web !== '') return false
}
if (key === 'web' && value !== '') {
return false
}
}
return true
},
Example input:
{
"livingroom": [],
"garage": [],
"outdoors": [],
"other": [],
"id": "ZI4hteKxgr",
"name": "Cuiti",
"description": "",
"user": "",
"date": "2016/5/13",
}
How to rewrite this function without using for loops?
I'm not 100% sure what you expect the code to do, because your existing code and your description differ.
Your description is, rephrased, that this function checks whether object.web or any object.XXX.web are undefined. Your code however assumes that all members are arrays and checks whether object.web or object.XXX[YYY].web are undefined. (Note that it also doesn't do it correctly and accesses .length even though the member in question might be undefined.)
Since I'm not sure which of those is right, I'm providing two answers.
Functionality as per your textual description:
function hasNoCategories(object) {
if(!object.web) return false;
return Object.keys(object).every(function(key) {
if(typeof object[key] !== 'object') return true;
return !!object[key].web;
});
}
Functionality as per your existing code: (but with the length property access fixed)
function hasNoCategories(object) {
if(!object.web) return false;
return Object.keys(object).every(function(key) {
if(!Array.isArray(object[key])) return true;
return object[key].every(function(el) {
if(typeof object[key] !== 'object') return true;
return !!el.web;
});
});
}
To understand how this works, check out the documentation on Object.keys (which returns an array with the names of all keys in your object) and Array.prototype.every (which runs a callback function for every element in an array and returns true only if the callback returned true for every element).
Note that I'm assuming that your "empty or undefined" should reject all kinds of falsy values including null and the number (not string) zero. If not, then all the checks like if(!something) and return !!something would need to be changed to if(typeof something === "undefined" || something === '') and return typeof something !== "undefined" && something !== '', respectively.
Side note to prevent nitpicking: Of course there are still loops going on. But it was specifically asked "without for loop" and there is no for in this code.
I assume this is what you are looking for:
var hasNoCategories = function(object) {
if (!object.web) {
return false;
}
for (let key in object) {
var value = object[key];
if (!value.web) {
return false;
}
}
return true;
};
I got rid of 1 loop. But this cannot be done without loops because you have to loop over all children. You can hide this loop inside some another function but you cannot get rid of it.
If you really don't want to use loops (I don't know why), one of your options is to serialize the object and phrase the string for the word "web".
var s = JSON.stringify(object);
var webIndex = s.indexOf('web');
Now perform some checks around this index to ascertain if that has the value 'undefined' or ''. Please keep in mind that the word "web" can match as a part of another property name too. So, you need to include this possibility too to your checks.

Object Comparing: check if an object contains the whole other object

I have two objects. Their structure looks a bit like this:
{
education: ["school", "institute"],
courses: ["HTML", "JS", "CSS"],
Computer: {
"OS":"WXP",
"WS":"NotePad"
}
}
The second:
{
education: ["school", "university", "institute", "collage"],
courses: ["HTML", "CSS", "JS", "Managing", "Directing"],
Computer: {
"OS":"WXP",
"WS":"NotePad",
"AV":"Avast"
},
something: function(){...},
other: "thing"
}
As you may noticed, the second object containes the whole first object, plus some items that the first one doesn't have.
I need to compare these two objects, and get an answer(true-false) if the second objects containes every single item of the first object.
true - if all of the items of the first object are also in the second one
false - if at least one of the items of the first object is not also in the second one, for example: if the second object wouldn't have the "css" course.
(The first one is requirements, the second is what the person has. I need to check if the person has all of the requirements)
Could be plain JS, jQuery, whatever. I prefer not to use server-side languages for that.
is there a way of doing that?
THANKS!
Just recursively check it:
function isContainedIn(a, b) {
if (typeof a != typeof b)
return false;
if (Array.isArray(a) && Array.isArray(b)) {
// assuming same order at least
for (var i=0, j=0, la=a.length, lb=b.length; i<la && j<lb;j++)
if (isContainedIn(a[i], b[j]))
i++;
return i==la;
} else if (Object(a) === a) {
for (var p in a)
if (!(p in b && isContainedIn(a[p], b[p])))
return false;
return true;
} else
return a === b;
}
> isContainedIn(requirements, person)
true
For a more set-logic-like approach to arrays, where order does not matter, add something like
a.sort();
b = b.slice().sort()
(assuming orderable contents) before the array comparison loop or replace that by the quite inefficient
return a.every(function(ael) {
return b.some(function(bel) {
return isContainedIn(ael, bel);
});
});
JavaScript (in ES5) has two composite native types (I'm assuming you don't have any custom collections in your code, if you do - I assume they support the 'old' iteration protocol (having .length)
Here is an annotated sketch of a solution. I did not run this - it's there to get you an idea of how to implement this algorithm. Note that this enters an endless loop for back references (var a = {}; a.a =a}).
function sub(big,small){
if(typeof big === "function") return small === big; // function reference equality.
if(big.length){ // iterable, for example array, nodelist etc. (even string!)
if(small.length > big.length) return false; // small is bigger!
for(var i = 0; i < small.length; i++ ){
if(!sub(big[i],small[i])){ // doesn't have a property
return false;
}
}
return true; // all properties are subproperties recursively
}
if(typeof big === "object" && big !== null){
// I assume null is not a subset of an object, you may change this, it's conceptual
if(typeof small !== "object" || small === null) return false;
for(var key in small){
// I consider the prototype a part of the object, you may filter this with a
// hasOwnProperty check here.
if(!sub(big[key],small[key])){ // doesn't have a property
return false;
}
return true;
}
}
return big === small; // primitive value type equality
// , or ES7 value type equality, future compat ftw :P
}
Edit: didn't notice that merge changes the first argument... changed the code, but it still would cause obj2 to change. You can add _.cloneDeep(obj2) which should take care of that, but by then my solution doesn't seem as elegant. Updated the demo with cloneDeep as well.
Edit2: Since JSON.stringify requires the order of object properties to be the same in the objects you compare, you could instead use something like Object comparison in JavaScript. However, in the demo you can see that it works, so I would say there is a good chance that for your case, using _.merge with JSON.stringify is reliable.
With lo-dash, you can use _.merge and check whether the result is the same as the larger object.
function(obj1, obj2) {
var obj3 =_.merge(_.cloneDeep(obj2), obj1);
return JSON.stringify(obj3) === JSON.stringify(obj1);
}
demo
Of course, another option would be to iterate over the entire object with vanilla JS.
// When order of objects is not same
function isContainedIn(a, b) {
if (typeof a != typeof b)
return false;
if (Array.isArray(a) && Array.isArray(b)) {
if(a.length == 1) {
var j=0;
while (j < b.length) {
if ((isContainedIn( a[0], b[j]))) {
return true;
}
j++;
}
return false;
} else {
var k=0;
while (k < a.length) {
if (!(isContainedIn([a[k]], b))) {
return false;
}
k++;
}
return true;
}
} else if (Object(a) === a) {
for (var p in a)
if (!(p in b && isContainedIn(a[p], b[p])))
return false;
return true;
} else
return a === b;
};
isContainedIn(requirements, person)
true
In addition to Benjamin's answer - you could test this:
const sub = (big, small) => {
if (typeof big === 'function' || typeof small === 'string') return small === big; // function or string reference equality
if (big && big.length) { // iterable, for example array, nodelist etc. (even string!)
if (small.length > big.length) return false; // small is bigger!
for (let i = 0; i < small.length; i++)
if (!sub(big[i], small[i])) // doesn't have a property
return false;
return true; // all properties are subproperties recursively
}
if (typeof big === 'object' && big !== null) {
// I assume null is not a subset of an object, you may change this, it's conceptual
if (typeof small !== 'object' || small === null) return false;
// console.log(Object.keys(small));
for (const key of Object.keys(small)) {
// I consider the prototype a part of the object, you may filter this with a
// hasOwnProperty check here.
if (sub(big[key], small[key]) === false) // doesn't have a property
return false;
continue;
}
return true;
}
return big === small; // primitive value type equality
};
or even use a much cleaner solution:
https://github.com/blackflux/object-deep-contain

How to identify anonymous types in JSON?

I am writing one function on Javascript which needs to address all the anynymous types in a JSON object.
For example,
Typed= {
emails: [{email:'a#a.com'}, {email:'b#a.com'}, {email:'c#a.com'}, {email:'d#a.com'}]
};
is an example of typed array in a JSON because each element inside the array is typed email
while,
Anon= {
emails: ['a#a.com', 'b#a.com', 'c#a.com', 'd#a.com']
};
is a JSON object where emails is collection of some anonymous objects.
Is there any ways that I can differentiate between both in JQuery or Javascript?
The simplest solution is to have the JSON source only return one of the two forms. Then you don't have to branch in your client.
If that's not an option, you could get the values out with JavaScript's handy lazy-evaluation of boolean expressions:
var em = json.emails[0].email || json.emails[0];
That statement will prefer the array-of-objects version, but use the array-of-strings version as a fallback.
(edited in response to clarifying comment below)
You can determine what properties a JS object has at runtime like this:
function enumerate(targetObject){
var props = [];
for (var propName in targetObject ){
props.push(propName);
}
return props;
}
console.log(enumerate({foo:1, bar:'baz'}),join(',')); //"foo, bar"
you could then modulate your logic on the basis of the properties you get back. You'll want to make sure you understand prototypes (specifically what Object.hasOwnProperty does and means), too.
You can use Array iteration methods to quickly check if all (or some) elements of the array have the desired type:
Anon.emails.every(function(e) { return typeof e == "object" }) // false
Typed.emails.every(function(e) { return typeof e == "object" }) // true
or a more generic solution
typeCheck = function(type) {
return function() {
return typeof arguments[0] == type
}
}
Anon.emails.every(typeCheck("object")) // false
Typed.emails.every(typeCheck("object")) // true
(An obligatory warning about iteration methods not being supported in ancient browsers)
How about this:
var istyped = function (a) {
if (typeof(a) !== 'object') {
return false;
}
var count = 0;
for (var key in a) {
count = count + 1;
}
return (count === 1);
}
I'm assuming here you just want to distinguish between regular variables (this would be your anonymous variable) and objects with just one key/value pair inside (this would be your typed variable).
To check if array contains only typed variables you'd just have to loop through it with that function. For example (in newer versions of JavaScript):
Typed.emails.every(istyped) = true
Anon.emails.every(istyped) = false
Why not do a map first:
emails = emails.map(function (email) {
if (typeof email.email === 'string')
return email.email;
});
That will make your emails array an array of just strings. Then you can just process it as usual. There aren't any side-effects if it is an array of strings (email.email will be undefined).
I do stuff like this when I have to make one client deal with multiple versions of an API. Alternatively, you could do the map the other way:
emails = emails.map(function (email) {
if (typeof email === 'string')
return {email: email};
});
This would work better if there could be other information in each object in your emails array.

How to determine if object is in array [duplicate]

This question already has answers here:
How do I check if an array includes a value in JavaScript?
(60 answers)
Closed 29 days ago.
I need to determine if an object already exists in an array in javascript.
eg (dummycode):
var carBrands = [];
var car1 = {name:'ford'};
var car2 = {name:'lexus'};
var car3 = {name:'maserati'};
var car4 = {name:'ford'};
carBrands.push(car1);
carBrands.push(car2);
carBrands.push(car3);
carBrands.push(car4);
now the "carBrands" array contains all instances.
I'm now looking a fast solution to check if an instance of car1, car2, car3 or car4 is already in the carBrands array.
eg:
var contains = carBrands.Contains(car1); //<--- returns bool.
car1 and car4 contain the same data but are different instances they should be tested as not equal.
Do I have add something like a hash to the objects on creation? Or is there a faster way to do this in Javascript.
I am looking for the fastest solution here, if dirty, so it has to be ;) In my app it has to deal with around 10000 instances.
no jquery
Use something like this:
function containsObject(obj, list) {
var i;
for (i = 0; i < list.length; i++) {
if (list[i] === obj) {
return true;
}
}
return false;
}
In this case, containsObject(car4, carBrands) is true. Remove the carBrands.push(car4); call and it will return false instead. If you later expand to using objects to store these other car objects instead of using arrays, you could use something like this instead:
function containsObject(obj, list) {
var x;
for (x in list) {
if (list.hasOwnProperty(x) && list[x] === obj) {
return true;
}
}
return false;
}
This approach will work for arrays too, but when used on arrays it will be a tad slower than the first option.
Why don't you use the indexOf method of javascript arrays?
Check this out: MDN indexOf Arrays
Simply do:
carBrands.indexOf(car1);
It will return you the index (position in the array) of car1. It will return -1 if car1 was not found in the array.
http://jsfiddle.net/Fraximus/r154cd9o
Edit: Note that in the question, the requirements are to check for the same object referenced in the array, and NOT a new object. Even if the new object is identical in content to the object in the array, it is still a different object.
As mentioned in the comments, objects are passed by reference in JS and the same object can exist multiple times in multiple structures.
If you want to create a new object and check if the array contains objects identical to your new one, this answer won't work (Julien's fiddle below), if you want to check for that same object's existence in the array, then this answer will work. Check out the fiddles here and in the comments.
Having been recently bitten by the FP bug reading many wonderful accounts of how neatly the functional paradigm fits with Javascript
I replicate the code for completeness sake and suggest two ways this can be done functionally.
var carBrands = [];
var car1 = {name:'ford'};
var car2 = {name:'lexus'};
var car3 = {name:'maserati'};
var car4 = {name:'ford'};
var car5 = {name:'toyota'};
carBrands.push(car1);
carBrands.push(car2);
carBrands.push(car3);
carBrands.push(car4);
// ES6 approach which uses the includes method (Chrome47+, Firefox43+)
carBrands.includes(car1) // -> true
carBrands.includes(car5) // -> false
If you need to support older browsers use the polyfill, it seems IE9+ and Edge do NOT support it. Located in polyfill section of MSDN page
Alternatively I would like to propose an updated answer to cdhowie
// ES2015 syntax
function containsObject(obj, list) {
return list.some(function(elem) {
return elem === obj
})
}
// or ES6+ syntax with cool fat arrows
function containsObject(obj, list) {
return list.some(elem => elem === obj)
}
try Array.prototype.some()
MDN Array.prototype.some
function isBiggerThan10(element, index, array) {
return element > 10;
}
[2, 5, 8, 1, 4].some(isBiggerThan10); // false
[12, 5, 8, 1, 4].some(isBiggerThan10); // true
You could use jQuery's grep method:
$.grep(carBrands, function(obj) { return obj.name == "ford"; });
But as you specify no jQuery, you could just make a derivative of the function. From the source code:
function grepArray( elems, callback, inv ) {
var ret = [];
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
if ( !inv !== !callback( elems[ i ], i ) ) {
ret.push( elems[ i ] );
}
}
return ret;
}
grepArray(carBrands, function(obj) { return obj.name == "ford"; });
I used underscore javascript library to tweak this issue.
function containsObject(obj, list) {
var res = _.find(list, function(val){ return _.isEqual(obj, val)});
return (_.isObject(res))? true:false;
}
please refer to underscore.js documentation for the underscore functions used in the above example.
note: This is not a pure javascript solution. Shared for educational purposes.
You can just use the equality operator: ==. Objects are checked by reference by default, so you don't even need to use the === operator.
try this, just make sure you're using the correct variable reference in the place of car1:
var i, car, l = cars.length;
for (i = 0; i < l; i++)
{
if ((car = cars[i]) == car1)
{
break;
}
else car = null;
}
Edit to add:
An array extension was mentioned, so here's the code for it:
Array.prototype.contains = Array.prototype.contains || function(obj)
{
var i, l = this.length;
for (i = 0; i < l; i++)
{
if (this[i] == obj) return true;
}
return false;
};
Note that I'm caching the length value, as the Array's length property is actually an accessor, which is marginally slower than an internal variable.
I would use a generic iterator of property/value over the array. No jQuery required.
arr = [{prop1: 'val1', prop2: 'val2'}, {prop1: 'val3', prop2: 'val4'}];
objectPropInArray(arr, 'prop1', 'val3'); // <-- returns true
function objectPropInArray(list, prop, val) {
if (list.length > 0 ) {
for (i in list) {
if (list[i][prop] === val) {
return true;
}
}
}
return false;
}
You could try sorting the array based on a property, like so:
carBrands = carBrands.sort(function(x,y){
return (x == y) ? 0 : (x > y) ? 1 : -1;
});
Then you can use an iterative routine to check whether
carBrands[Math.floor(carBrands.length/2)]
// change carBrands.length to a var that keeps
// getting divided by 2 until result is the target
// or no valid target exists
is greater or lesser than the target, and so on, which will let you go through the array quickly to find whether the object exists or not.
try this ,
You can use the JavaScript some() method to find out if a JavaScript array contains an object.
<script>
// An array of objects
var persons = [{name: "Harry"}, {name: "Alice"}, {name: "Peter"}];
// Find if the array contains an object by comparing the property value
if(persons.some(person => person.name === "Peter")){
alert("Object found inside the array.");
} else{
alert("Object not found.");
}
</script>
EDIT 05/18/2022
The most simple way using ES6:
const arrayContainsObject = <T extends Record<string, unknown>>(array: T[], object: T) => {
return array.some(item => Object.keys(item).every(key => item[key] === object[key]))
}
Use like so:
const arr = [{
prop1: 'value1',
prop2: 'value2'
}]
const obj1 = {
prop1: 'value1',
prop2: 'value2'
}
const obj2 = {
prop2: 'value2',
prop1: 'value1'
}
const obj3 = {
prop0: 'value0',
prop1: 'value1'
}
arrayContainsObject(arr, obj1) // true
arrayContainsObject(arr, obj2) // true, even when props are arranged in different order
arrayContainsObject(arr, obj3) // false
Previous answer, don't use (because the order of props in an object needs to be identical)
const arr = [{
prop: 'value'
}]
const obj = {
prop: 'value'
}
arr.some((e) => Object.entries(e).toString() === Object.entries(obj).toString()) // true
i know this is an old post, but i wanted to provide a JQuery plugin version and my code.
// Find the first occurrence of object in list, Similar to $.grep, but stops searching
function findFirst(a,b){
var i; for (i = 0; i < a.length; ++i) { if (b(a[i], i)) return a[i]; } return undefined;
}
usage:
var product = $.findFirst(arrProducts, function(p) { return p.id == 10 });
This function is to check for a unique field.
Arg 1: the array with selected data
Arg 2: key to check
Arg 3: value that must be "validated"
function objectUnique( array, field, value )
{
var unique = true;
array.forEach(function ( entry )
{
if ( entry[field] == value )
{
unique = false;
}
});
return unique;
}
you can use Array.find().
in your case is going to look like this
carBrands.find(function(car){
let result = car.name === 'ford'
if (result == null){
return false;
} else {
return true
}
});
if car is not null it will return the javaScript Object which contains the string 'ford'
The issue with many of the answers here is that they will NOT find an object in an array that is equal to another object. They will only search for an EXISTING object that has a pointer to it in an array.
Quick fix using lodash to see if ANY equal object is in an array:
import _ from 'lodash';
_.find(carBrands, car1); //returns object if true, undefined if false
Working Plunker using this method: https://plnkr.co/edit/y2YX9o7zkQa2r7lJ
if its possible to use es6
carBrands.filter(carBrand => carBrand.name === carX.name).length > 0
if it's true there is a similarity
You can convert both the JSON objects to string and simply check if the bigger json contains the smaller json.
console.log(JSON.stringify(carBrands).includes(JSON.stringify(car1))); // true
console.log(JSON.stringify(carBrands).includes(JSON.stringify(car5))); // false
You could also a the findIndex
var carBrands = [];
var car1 = {name:'ford'};
var car2 = {name:'lexus'};
carBrands.push(car1);
if (carBrands.findIndex(f => f.name === car1.name) === -1) {
console.log('not contain')
} else {
console.log('contain')
}
if (carBrands.findIndex(f => f.name === car2.name) === -1) {
console.log('not contain')
} else {
console.log('contain')
}

Categories

Resources