Only add even numbers to Array using JavaScript Proxy - javascript

I'm learning JavaScript Proxy .
By using set trap i only want to add even number to Array. But every time throw
Uncaught TypeError: 'set' on proxy: trap returned falsish for property
'length'
Here is my code example.
//Only even numbers will be added here. We are using Js Proxy
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set(target, p, value, receiver) {
if ( (value % 2) === 0 ) {
target[p] = value;
return true
}else {
return false;
}
}
});
evenNumbers.push(2);

You neet to return true for both cases, because
Return value
The set() method should return a boolean value.
Return true to indicate that assignment succeeded.
If the set() method returns false, and the assignment happened in strict-mode code, a TypeError will be thrown.
push calls the proxy twice, one by pushing the value and another times to change the length property. To avoid unnecessary more action whithin in this proxy, you could exit early with this unwanted property.
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set (target, p, value, receiver) {
if (p === 'length') return true; // exclude this property
if (value % 2 === 0) {
target[p] = value;
}
return true;
}
});
evenNumbers.push(2);
console.log(evenNumbers.length); // 1
evenNumbers.push(3);
console.log(evenNumbers.length); // Still 1
console.log(evenNumbers); // [ 2 ]

The issue is that the function .push() tries to update the length (evenNumbers.length = 1), and that's not being allowed by the trap.
Try this:
let evenNumbers = [];
evenNumbers = new Proxy(evenNumbers, {
set(target, p, value, receiver) {
if ( isFinite(p) ) {
// The key is a number (index), so there's code trying to add/change an element of the array.
if ( (value % 2) === 0 ) {
target[p] = value;
return true
}else {
return false;
}
} else {
return true;
}
}
});
evenNumbers.push(2); // OK
evenNumbers.push(3); // TypeError

Related

How to use Object.prototype.valueOf to differ return type as a result of coercion in equal value and equal type operator?

A question says to make a sum function that meets the required output as:
My question is, under the circumstance, is it possible to reach the result?
const sum1 = sum(1)
sum1(2)() // return 3
sum1(3)() // return 4
// call directly without assigning to a variable
sum(1)(2)(3)() // return 6
sum(5)(-1)(2)() // return 6
sum(1)(2)(4) == 7 // true
sum(1)(2)(4) === 7 // false
And the definition to the sum function:
/**
* #param {number} num
*/
function sum(num) {
// your code here
}
It also hints that a decent understand of Object.prototype.valueOf() is crucial.
I write something like the code below, also a script for testing and to show my perspective about the required returns. I think Object.prototype.valueOf works when I call the return for as a primitive type, especially a value. (Refers to ECMAScript spec)
My opinion about the question are:
To meet the test case - sum(1)(2)(4) == 7 -> true, useSum.prototype.valueOf is necessary, but I found that I have to call new for make it work. However, this operation also results an object instead of a function.
Then I use conditionals to return function if there is an argument or to return plain value.
While I passed some test cases, the test case - sum(1)(2)(4) == 7 -> true turn to be failed.
I also failed in the cases to run sum1(3)() === 4 after running sum1(2)() === 3. The this.accumulator does not know that it's came from sum1 which has an initial base of 1.
Dealing with the problem than raising another problem for this.accumulator leaves value that affect the initial value in calc the test cases: sum(1)(2)(3)() === 6, sum(5)(-1)(2)() === 6.
/**
* #param {number} num
*/
function sum(num = 0) {
if (this.accumulator) {
this.accumulator += num;
} else {
this.accumulator = num;
this.init = num;
}
const that = this
function useSum(isEnd) {
if (!isEnd) {
return (addend) => sum(addend)
}
return that.accumulator
}
if (num) {
useSum.prototype.valueOf = function() {
return that.accumulator;
}
const result = new useSum(false);
return result
}
const result = this.accumulator;
this.accumulator = 0;
return result
}
// test function
function expect(caseName, occasion) {
function toBe(result) {
console.log(`test case: ${caseName}`);
if (occasion === result) {
console.log("--> pass\n")
} else {
console.log(`--> fail, expect ${result}, got ${occasion}\n`)
}
return null
}
return { toBe }
}
// test cases from my pespective
const sum1 = sum(1);
expect("sum1(2)() === 3", sum1(2)()).toBe(3)
expect("sum1(3)() === 4", sum1(3)()).toBe(4)
expect("sum(1)(2)(3)() === 6", sum(1)(2)(3)()).toBe(6)
expect("sum(5)(-1)(2)() === 6", sum(5)(-1)(2)()).toBe(6)
expect("sum(1)(2)(4) == 7 -> true", sum(1)(2)(4) == 7).toBe(true)
expect("sum(1)(2)(4) === 7 -> false", sum(1)(2)(4) === 7).toBe(false)
My appreciation for any article, idea for solving the problem.
It's hard to point out what specifically you should change in you answer to make it work. When I look at the question at hand my mind immediately jumps to a recursive function that uses variable/parameter scoping to store the values.
const sum1 = sum(1)
console.log(sum1(2)()) // return 3
console.log(sum1(3)()) // return 4
// call directly without assigning to a variable
console.log(sum(1)(2)(3)()) // return 6
console.log(sum(5)(-1)(2)()) // return 6
console.log(sum(1)(2)(4) == 7) // true
console.log(sum(1)(2)(4) === 7) // false
function sum(a) {
if (!arguments.length) return;
function addA(b) {
if (!arguments.length) return a;
return sum(a + b);
};
Object.defineProperty(addA, "valueOf", {
enumerable: false,
configurable: true,
writable: true,
value() { return a },
});
return addA;
}
In the answer above !arguments.length in sumA is used to check if parameters are passed. If no parameters are passed a is returned, which holds the final sum value.
If there there is a parameter present sum(a + b) is returned which in turn returns a new addA function. This new function has it's a argument updated to hold the new value.
After creating the function sumA we give it a custom valueOf method that returns the current sum, this is used for type coercion. This is needed to let sum(1)(2)(4) == 7 evaluate properly. I've added this property as a non-enumerable property using Object.defineProperty(), but it can be simplified if you don't mind valueOf being enumerable.
To use an enumerable property change:
Object.defineProperty(addA, "valueOf", {
enumerable: false,
configurable: true,
writable: true,
value() { return a },
});
Into:
addA.valueOf = () => a;
const sum1 = sum(1)
console.log(sum1(2)()) // return 3
console.log(sum1(3)()) // return 4
// call directly without assigning to a variable
console.log(sum(1)(2)(3)()) // return 6
console.log(sum(5)(-1)(2)()) // return 6
console.log(sum(1)(2)(4) == 7) // true
console.log(sum(1)(2)(4) === 7) // false
function sum(a) {
if (!arguments.length) return;
function addA(b) {
if (!arguments.length) return a;
return sum(a + b);
};
addA.valueOf = () => a;
return addA;
}
Note that a function can be treated as a normal JavaScript object and can be assigned properties like valueOf, or any other property for that matter.

Set Trap execute for two times - JS Proxy

Im working with proxies in js, but something is weird =>
let _usernames = [];
_usernames = new Proxy(_usernames, {
set(target, prop, val) {
console.count(); //this execute for two times!
if(typeof val === 'string') {
target[prop] = val;
return true;
} else {
return false;
}
}
});
_usernames.push('Manuel');
The Set trap should call only once when i push to the array, but it executed twice.
And there is an error in the console, when i push to array =>
Uncaught TypeError: proxy set handler returned false for property '"length"'
How can i fix this and what's wrong?
Calling Array#push causes set to be called two times:
target=[], prop=0, val=Manuel: Adds a new value to an index
target=["Manuel"], prop=length, val=1: Updates length of array
In your case, the second call is returning false since the length value is numeric.
A possible solution:
let _usernames = [];
_usernames = new Proxy(_usernames, {
set(target, prop, val) {
console.log(target, prop, val);
if(typeof val === 'string' || prop === 'length') {
target[prop] = val;
return true;
} else {
return false;
}
}
});
_usernames.push('Manuel');

How to separate rules array and execute functions from that array

I have a class for validation which have different validation functions.
Now what i am trying to do is make an object in another file and send all the validation data using a constructor.This constructor will receive an object that looks like this "rules[is_empty:value]". In this left part is function name and value is the value fetched from input field.Now how do I call the function and send the value as an argument.Also what should i do when there are functions that has more than one argument.
I have already tried using map method and split method but not able to access the function.
class Validator {
constructor(rules) {
let rule_obj = {}
// rules[is_empty:value,has_valid_min_length:2;3]
}
/**this is to check if the field has an empty value or not */
is_empty = (value) => {
if (value == '' || value == null) {
return true
}
else {
return false
}
}
/**this is to check if value has required minimum length or not */
has_valid_min_length = (value, min_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length < min_length) {
return false
}
else {
return true
}
}
/**this is to check if value has more characters than maximum length */
has_valid_max_length = (value, max_length = 0) => {
if (this.is_empty(value)) {
return false
}
if (value.length > max_length) {
return false
}
else {
return true
}
}
//this is to check if selected date is less than given limit
is_before_min_date = (value_date, min_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date < min_date) {
return true
}
else { return false }
}
//this is to check if selected date is higher than given limit
is_after_max_date = (value_date, max_date) => {
if (this.is_empty(value)) {
return false
}
if (value_date > max_date) {
return true
}
else {
return false
}
}
}
I want to call the function which is before ':' sign in the array and give that function argument which is in value that is at the right side of ':'.
Please help.
You could send an object through the constructor...
x = {
"is_empty": [0, 2, 2],
"has_valid_min_length": [ [value, min_length], [value, min_length] ],
"has_valid_max_length": [ [value, max_length], [value, max_length] ],
"is_before_min_date": [ [value_date, min_date], [value_date, min_date] ],
"is_after_max_date": [ [value_date, max_date], [value_date, max_date] ]
}
and then in your constructor, set up a loop through the object and value arrays...
constructor(to_be_validated) {
let validator_names = Object.keys(to_be_validated);
for (let validator of validator_names) {
let values = to_be_validated[validator];
if (validator === "is_empty") {
for (let value of values) {
this.is_empty(value);
}
} else if (validator === "has_valid_min_length") {
for (let value of values) {
this.has_valid_min_length(value[0], value[1]);
}
} etc...
}
}
and then when you call the function, the methods should execute
let my_validator = new Validator(x);
I echo the comment(s) above. Wanting the approach and specific syntax of,
let my_validator = new Validator(rules[validator_name:values]);
all in one clean line like that, is a bit off. I've never seen it done like that.
You probably want an additional function in your class that calls all the check-functions according to your rules.
class Validator {
constructor(rules) {
this.rules = rules;
// rules={is_empty:[],has_valid_min_length:[2]};
}
validate = (value) => {
let pass = true;
Object.keys(this.rules).forEach(k=>pass = pass && this[k](value, ...this.rules[k]));
return pass;
}
...
}
The rules-Objects has key-value-pairs, where the keys are the function-names of the individual checks and the values are arrays of parameters that will be passed. This array can have zero or more entries.
Edit: forEach will iterate over all the keys. On every turn k will hold the current key. pass is a boolean that collects all the return values of your checker-functions. (true && false === false) That way the final return-value will be false if any of the checks returned false.
You would then apply the rules to your data like this:
myValidatorObj.validate(data);

Using Proxies with plain arrays

Consider this code:
app.mediaLibrary = new function MediaLibrary() {
var self = this;
self.media = new Proxy([], {
set(target, key, value) {
console.log(`Setting value ${key} as ${value}`)
if (!(value instanceof self.Media))
throw new Error(`${value} is not instance of Media.`)
target[key] = value;
return true
},
get (target, key) {
console.log(`Getting value ${key} as ${target[key]}`)
return target[key]
}
});
self.Media = function Media(file, fileReader) {
this.src = fileReader.result;
this.file = file;
}
return self;
}
Whenever I call app.mediaLibrary.media.push(new app.mediaLibrary.Media("", ""))
In console I see this:
Getting value push as function push() { [native code] }
Getting value length as 0
Setting value 0 as [object Object]
Setting value length as 1
Uncaught Error: 1 is not instance of Media.(…)
I understand why I see this but how can I code around it? It seems my traps are triggered by internal(push,length) as well as external calls([0]=...) and I don't know how to differentiate between them. Any ideas?
I think you are asking the wrong question. This isn't about internal vs external calls, this is about the specific object that you are proxying: An array.
You could go with three conditions:
Length can be set to anything (only the array will handle this anyway)
Numeric properties can only be set to Media instances.
All other properties are off limits.
You could write that like this:
app.mediaLibrary = new function MediaLibrary() {
var self = this;
self.media = new Proxy([], {
set(target, key, value) {
console.log(`Setting value ${key} as ${value}`)
// Check if this is a valid array index
// See http://www.ecma-international.org/ecma-262/6.0/#sec-array-exotic-objects
if (String(key >>> 0) === key && (key >>> 0) != Math.pow(2, 32) - 1) {
if (!(value instanceof self.Media))
throw new Error(`${value} is not instance of Media.`);
} else if(key !== 'length') {
throw new Error(`${key} may not be written to.`);
}
target[key] = value;
return true
},
get (target, key) {
console.log(`Getting value ${key} as ${target[key]}`)
return target[key]
}
});
self.Media = function Media(file, fileReader) {
this.src = fileReader.result;
this.file = file;
}
return self;
}
If you really need to differentiate between .push and [0]=..., then no, it can't be done.

Object has-property-deep check in JavaScript

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
});

Categories

Resources