I'm trying to discover if an object has some properties and I'm having trouble using the hasOwnProperty method.
I'm using the method on an array (I know the documentation states a string).
The following line returns true:
{ "a": 1, "b": 2 }.hasOwnProperty( ["a"]);
This line returns also true:
{ "a": 1, "b": 2 }.hasOwnProperty( "a", "b");
But this one returns false:
{ "a": 1, "b": 2 }.hasOwnProperty( ["a", "b"])
And I need it to return true. I'm using Object.keys(object) to get the properties that I'm using, and it returns me an array, so I need to use an array on hasOWnProperty.
Is there some theoric concept I'm missing? And is there some way to fix this problems?
There are two things going on here.
First, hasOwnProperty only takes one argument. So it'll ignore whatever other arguments you pass to it.
Second, (and I'm simplifying slightly here) it's going to convert that first argument to a String, and then check to see if the object has that property.
So let's look at your test cases:
The reason { "a": 1, "b": 2 }.hasOwnProperty( "a", "b"); returns true is because it's ignoring the second argument. So really it's just checking for "a".
{ "a": 1, "b": 2 }.hasOwnProperty( ["a", "b"]) returns false because the first argument, ["a", "b"], gets converted to "a,b", and there's no { "a": 1, "b": 2 }["a,b"].
To find out if a given object has all the properties in an array, you can loop over the array and check each property, like so:
function hasAllProperties(obj, props) {
for (var i = 0; i < props.length; i++) {
if (!obj.hasOwnProperty(props[i]))
return false;
}
return true;
}
Or, if you're feeling fancy, you can use the every function to do this implicitly:
var props = ["a", "b"];
var obj = { "a": 1, "b": 2 };
var hasAll = props.every(prop => obj.hasOwnProperty(prop));
I hope that helps clarify things. Good luck!
If you want to check the object's own properties, you could use the Object.getOwnPropertyNames method. It returns an array of all properties (including non-enumerable properties except for those which use Symbol) found directly upon a given object.
let o = { "a": 1, "b": 2 };
Object.getOwnPropertyNames(o).forEach(k => console.log(`key: ${k}, value: ${o[k]}`));
Given the documentation, it seems that the hasOwnProperty() method takes either a string or a symbol as an argument. So I think hasOwnProperty() isn't capable of taking a collection of strings and testing if the object has each one as a property.
I think another approach would be to take the array of strings and iterate through each one. Then for each string in your array (the properties you want to test for), you could test if the object has that property. Here's an example:
const o = new Object();
var propsToTest = ['a', 'b'];
o.a = 42;
o.b = 40;
var hasProperties = true;
propsToTest.forEach(function(element) { // For each "property" in propsToTest, verify that o hasOwnProperty
if(!o.hasOwnProperty(element))
hasProperties = false;
});
console.log(hasProperties);
First, in terms of getting your third snippet to return true, I don't think that's possible. The best you could do would be to check each property individually:
const obj = { "a": 1, "b": 2 };
console.log(["a", "b"].every(p => obj.hasOwnProperty(p)))
Which should give you what you want.
But to understand why the first two returned true, but the third false:
The hasOwnProperty method only accepts a single argument (additional arguments are ignored), and it expects that argument to be a string. If the argument is not a string, then JavaScript will attempt to coerce it to one (usually by using a .toString method).
So your first two snippets are equivalent because:
["a"].toString() === "a", so hasOwnProperty(["a"]) is the same as hasOwnProperty("a") after the argument is converted to a string.
Then in your second snippet, the second argument "b" is simply ignored, leaving it equivalent to just hasOwnProperty("a") again.
And finally, your third snippet uses ["a", "b"], and ["a", "b"].toString() === "a,b", which is not a property of your object.
So this is an old question and I'm kinda surprised no one else has thought to write it but this is usually solved using .every on the array. So for the original question, the code should be something like:
["a", "b"].every((item) => ({ "a": 1, "b": 2 }.hasOwnProperty(item)))
This will return a simple true.
Array .every will run a condition for every item in an array and only return true if the condition is true for all item.
See the the mozilla web docs
You can achieve this with a for...in loop like this
const obj = { "a": 1, "b": 2 };
for (key in obj) {
if (obj.hasOwnProperty(key)) {
console.log('has', key, obj[key]);
} else {
console.log('not', key, obj[key]);
}
}
Related
While I was trying to check how many times an element used in an array, I found this code. It is written by another user and I got it to work but I am trying to figure out why he used "{}" at the end. I know that .reduce() method can get initialValue but I could not understand the use of braces.
var a = ["a","b","b","c","a","b","d"];
var map = a.reduce(function(obj, b) { obj[b] = ++obj[b] || 1;
return obj;
}, {});
I thought that they might be the initialValue parameter since it covers the result, but when I tried to remove the braces the result was not the same. I also checked the MDN documents, found some similar code but could not wrap my mind around it since I am quite new in JavaScript.
When we use the braces I get :
{
a: 2,
b: 3,
c: 1,
d: 1
}
But when I remove the braces and run it I get:
a
I tried using brackets and it resulted as : [ a: 2, b: 3, c: 1, d: 1 ],
So it seems the braces enclose the values but shouldn't it work as usual without braces?
But when I remove the braces and run it, I get: a
This is the syntax:
arr.reduce(callback[, initialValue])
initialValue : Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the array will be used."
So, if you use reduce without the initialValue({}), the first item in the array will be used as the initialValue which is "a"
So, it becomes similar to:
var a = ["a", "b", "b", "c", "a", "b", "d"];
var map = a.slice(1).reduce(function(obj, b) {
obj[b] = ++obj[b] || 1;
return obj;
}, "a");
console.log(map)
In the first iteration,
obj[b] = ++obj[b] || 1;
becomes
"a"["b"] = ++"a"["b"] || 1
This neither throws an exception, nor does it change the obj string. obj is still "a" and it will be returned every time.
The braces {} represent a new empty object in javascript, In your case, it will be the object returned by the reduce method to the map variable, we need to initialize it first then fill it in the core of the reduce callback.
Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the array will be used. Calling reduce() on an empty array without an initial value is an error.
It's the initialValue take a look to reduce(), here's a sample, If you were to provide an initial value as the second argument to reduce(), the result would look like this:
let arr = [0, 1, 2, 3, 4];
arr = arr.reduce((accumulator, currentValue, currentIndex, array) => {
return accumulator + currentValue;
}, 10);
console.log(arr);
That is the accumulator object.You can say that it is the initial value so when the call back function will be executed the initial value will be a empty object.
So in the example below initially it is passing an object which have key e
var a = ["a", "b", "b", "c", "a", "b", "d"];
var map = a.reduce(function(obj, b) {
console.log(obj)
obj[b] = ++obj[b] || 1;
return obj;
}, {e:'test'});
console.log(map)
The second argument in the .reduce() method is the initialValue, which is a
Value to use as the first argument to the first call of the callback. If no initial value is supplied, the first element in the array will be used.
tl;dr
It's the initial value which .reduce() starts with. This is the first argument passed to the callback in the first call.
In your case the idea was to build a map of values from the array where keys in the map were the values from the array and values in the map were a number of occurrences of that value in the array.
A map in JS can be easily simulated by an object which in your case has been passed as a literal {} to the .reduce() method. The method fires the callback for each element in the array passing the result object as the first argument and the current element in the array as the second. But the problem is at the first call - what value should be used as the result object if there were no previous elements in the array to accumulate? That's why you need to pass some initial value to have something to start with. As the MDN states, if no initialValue is passed, the first element of the array is used - that's why you got a when removed initial value. When you passed [] you told JS to have an array literal as the initial value but in the callback, you treat it as an object which is allowed in JS since an array is also an object. The problem arises when you try to iterate over those properties or stringify them using JSON.stringify(). But it's for another story ;)
{} create new object if you don't add this then the first element in the array will be used.
You can see that when you run the code with {} you get an empty object as the initialValue and fulfills your requirement.
var a = ["a","b","b","c","a","b","d"];
var map = a.reduce(function(obj, b) {
"use strict";
if (Object.entries(obj).length === 0 && obj.constructor === Object) {
console.log("InitialValue is defined as object: ", obj);
}
obj[b] = ++obj[b] || 1;
return obj;
}, {});
console.log(map);
Whereas without {} it assigns the first value of array a to the obj that means now obj is a string and when you try to use it as an object then it throws error as in the below code.
var a = ["a","b","b","c","a","b","d"];
var map = a.reduce(function(obj, b) {
"use strict";
console.log("InitialValue not defined: ", obj);
obj[b] = ++obj[b] || 1;
return obj;
});
console.log(map);
I have just added "use strict" to show this error.
Sample Code:
const a = {val: 1};
const b = {val: 2};
const list = [a, b];
console.info(list.includes(a)); // true
console.info(list.includes({val: 1})); // false
Questions:
Why does the second statement evaluate as false?
How can I properly use this method to search for a specific object in an array of objects?
TL;TR
list.some(value => JSON.stringify(value) === JSON.stringify({val: 1}));
Answers:
First, variable a is a reference link to object. If you checking using list.includes(a) it returns true because it found link to same object you declared previously const a = {val: 1};.
Second, list.includes({val: 1}) returns false, because you are trying to search for reference to newly created object - {val: 1}. Object may contain same values and structured the same, but they store in memory as totally different objects.
If you want to check same object by structure, use Array.prototype.some() and write comparator function for your case and logic.
This basically boils down to this:
{ val: 1 } === { val: 1 } // false
Objects in javascript are compared by reference, and as the objects are at different positions in memory, they are not the same. To check for an object that has val set to 1 ypu have to manually search through all objects:
if(list.some(el => el.val === 1))
I have a question here. I know my code have plenty of problems and I need your help.
The problem is, To return an object which have numbers of repetition information of the string.
The given string is
var r = countFreq(["a", "b", "b", "c", "c", "c", "d"]);
and the result have to be
{"a":1, "b":2, "c":3, "d":1 }
by console.log(r);
All that I know is, key(properties) have to be element and value(value of property) have to be the number of repetition.
AND, I must use 'in' key world to solve this problem.
Like,
if('property' in 'object') {
//...
}else {
//...
}
(if there's no property initialize as 1, and if there's a same property, add 1 each time)
I really appreciate your help.
(This post may have grammatical errors. I really feel sorry about that...)
function countFreq(array) {
var i;
for(i=0; i<array.length; i++)
{
if(array[i] in array)
{
return i += 1;
}else
{
return i = 1;
}
console.log(array[i]+": "+i+", ");
}
}
var r = countFreq(["a","b","c","c","c","d"]);
console.log(r);
According to MDN - The 'in' operator returns true if the specified property is in the specified object or its prototype chain.
Prop is a string or symbol representing a property name or array index (non-symbols will be coerced to strings).
Object is to check if it (or its prototype chain) contains the property with specified name.
So in your case, it depends what your object is? if you object is an array, you need to use prop as properties of array. All index values up to length of array will return true.
MDN Example of arrays
var trees = ['redwood', 'bay', 'cedar', 'oak', 'maple'];
0 in trees // returns true
3 in trees // returns true
6 in trees // returns false
'bay' in trees // returns false (you must specify the
// index number, not the value at that index)
'length' in trees // returns true (length is an Array property)
Symbol.iterator in trees // returns true (arrays are iterable, works only in ES2015+)
I think you are misunderstanding the in operator. In can be used in 2 cases, as a boolean operator to check for the presence of an index in an array or to iterate over the indexes of an array with a for loop. You are using it to check for the presence of a value in an array directly, which you cannot do. Also you are returning from the function after each iteration so you will only ever get 1 or 0.
I presume you want something like the following:
countFreq(array) {
var results = { a: 0, b: 0, c: 0, d: 0 };
for (var index in array) {
results[array[index]] ++;
}
return results;
}
Now you can access each result with results[‘a’] for instance, after calling countFreq. I think you need to read up on return and loops in JavaScript.
I count number of result from database and send as 'TotalItems'
mysql_crawl.query('SELECT COUNT(*) FROM `catalogsearch_fulltext` WHERE MATCH(data_index) AGAINST("'+n+'") ', function(error, count) {
var r = count[0];
var totalItems = r,
res.render('result.html', {
totalItems: totalItems
})
});
I try running console.log on r, result is
RowDataPacket { 'COUNT(*)': 25 }
but when I run <% totalItems %> on javascript, it show as
[object Object]
How can I show object as number?
totalItems is object, you can access its values by totalItems['COUNT(*)']
Telling a template engine to render an object will cast the object into a string. This is done by looking at the object to see if it implements a toString() method and if not will walk the object's prototype chain till it finds one.
In you example the closest toString is on the top level primitive Object. This implementation simply outputs [object Object]. There is excellent information about this is on MDN.
You could implement your own toString and even wrap the count object in a custom object with a name and custom toString. But in your case you were given a generic object and am (presumably) not at the level of abstracting code in more object oriented design patterns. In this case it is simply easier to wrap your template code use with JSON.stringify which will serialize any object into a string containing JSON data which is human readable for developers:
<% JSON.stringify(totalItems, null, 2) %>
[object Object] comes from the .toString() function. Example: { a: 1 }.toString().
You need to encapsulate it in JSON.stringify(...), example: JSON.stringify({ a: 1 }) => { "a": 1 }.
For full printing of an Object or Array, you can use:
JSON.stringify(obj, null, 4);
This returns a String representation of the given Object as a tree.
For example:
{a: 1, b: 2, c: {d: 4}}
becomes:
{
"a": 1,
"b": 2,
"c": {
"d": 4
}
}
The list processing routine map on an array object is very convenient at times. Here's one of the handy ways to use it:
var numarr = [1,2,3,4];
console.log(numarr.map(String))
>>> ["1", "2", "3", "4"]
I took this for granted thus far. Today I was however puzzled by it. What the map function is returning above is an array of strings. We typically pass a function to map as argument. In above case we pass String object. String is implemented inside the Javascript implementation, so I don't know what kind of specialities it has. The above code works as if a new instance of String is created for each item in array.
If it's not clear, consider this. If I decide to implement an object in Javascript say MyString and pass it to map, I won't get the above behavior.
function MyString(x) { this.val = x; }
MyString.prototype.toString = function () { return String(this.val); };
var ms = new MyString(4)
console.log(String(ms));
>>> "4"
var arr = [1,2,3];
arr.map(MyString)
>>> [undefined, undefined, undefined]
Does anyone know why then arr.map(String) works the way it does?
Update: A comment I added below clarifies my question better.
At the end of the 2nd snippet, try console.log(val). You'll notice you've leaked a global:
var arr = [1,2,3];
arr.map(MyString);
console.log(val); // "3"
When using arr.map(MyString), you're calling that constructor as a function, without the new to create instances. And, since MyString doesn't return anything, you get undefined in the results. But, you've still set this.val, while this isn't an instance but is rather the global object.
String doesn't return undefined because it has a return when called without new:
When String is called as a function rather than as a constructor, it performs a type conversion.
Returns a String value (not a String object) computed by ToString(value). If value is not supplied, the empty String "" is returned.
You can imitate this with MyString by checking if this is an instance first, returning a new instance when this isn't one already:
function MyString(x) {
if (this instanceof MyString) {
this.val = x;
} else {
return new MyString(x);
}
}
var arr = [1, 2, 3];
arr.map(MyString); // [ {val: "1"}, {val: "2"}, {val: "3"} ]
Array.map returns an array whose elements are the value returned by applying the specified function to each value in the this array. String is a function; it returns a string. That's all there is to it.
Thats is because String is a function. It returns a string constructed from what is passed to it. For example, if you call String(100), it will return "100".