Extend has proven challenging because I'm trying to manipulate the arguments object.
My current extend function does not account for more than one argument after the initial given object
_.extend = function(object, sources) {
return _.reduce(sources, function(memo, current) {
memo[Object.keys(sources)] = current || 0;
return memo;
}, object)
}
At first, I tried to make a copy of the arguments object, turn it into an array, and shift() the first argument out. This is terribly inelegant, but has still proven to be ineffective
I realized that apply() or call() should be used in this context somehow. When I tried it, it also didn't work.
_.extend = function(object) {
var copy = [].slice.call(arguments);
copy.shift();
doIT.apply(this, copy);
var doIt = function(copy) {
return _.reduce(copy, function(memo, current) {
memo[Object.keys(copy)] = current || 0;
return memo;
}, object)
}
doIt(copy);
}
Any ideas on what I can do to fix this?
There's no need to use apply on your function, just call it normally. By doing so, you are passing your shifted array as a list of arguments into your function. Since you are only working with copy in your parameter list then only one argument would make it. There's no need for call either, as you can easily invoke the function since no this context is required. Lastly, since your function is not a function declaration, it won't be available until the expression holding the function has been evaluated (available after var doIt).
Now, your doIt function is doing something wrong:
memo[Object.keys(copy)] = current || 0;
Object.keys(copy) returns an array with copy keys. So you are doing:
memo[array] = current || 0;
The array will be casted to a string, but it's definitely not something you want either. What you need to is iterate each element properties of the copy array (each on is on current) and copy those properties into your first object (memo accumulator). Like this:
var extend = function (object) {
var copy = [].slice.call(arguments);
copy.shift();
var doIt = function (copy) {
return copy.reduce(function (memo, current) {
Object.keys(current).forEach(function (key) {
memo[key] = current[key];
});
return memo;
}, object)
}
return doIt(copy);
}
However, extends still need to handle the appropriated getters and setters. So you'll need to do something like:
var extend = function (object) {
var copy = [].slice.call(arguments);
copy.shift();
var doIt = function (copy) {
return copy.reduce(function (memo, current) {
Object.keys(current).forEach(function (key) {
var pDesc = Object.getOwnPropertyDescriptor(current, key);
Object.defineProperty(memo, key, pDesc);
});
return memo;
}, object)
}
return doIt(copy);
}
Related
If I have an arrow function like:
let fn = x => x.contact.name;
Then, I can re-use that fn to retrieve the value as:
function read(obj) {
return fn(obj)
}
But how can I set values of an object using the property expression in fn?
function write(obj, newVal) {
// intending to set obj.contact.name = newVal using fn
}
Edit:
(To give a little bit of background)
I am writing a JS library where consumers would provide a lambda expression like above (fn), and I provide a read/write functionality somewhere based on the expression. Right now I am using fn.toString() and string manipulation as a temporary solution.
Normally, lenses need two callbacks, get and set, because (in javascript at least) functions are not permitted to return references. For simple cases, like getting/setting a property, you can provide a shortcut function that would return a getter/setter pair. For example:
function prop(path) {
return {
get(obj) {
return path.split('.').reduce((o, p) => o[p], obj)
},
set(val) {
return function (obj) {
let p = path.split('.')
let k = p.pop()
let r = p.reduce((o, p) => o[p], obj)
r[k] = val
}
}
}
}
//
obj1 = { contact: { name: 'one' }}
obj2 = { contact: { name: 'two' }}
let lens = prop('contact.name')
console.log([obj1, obj2].map(lens.get));
[obj1, obj2].forEach(lens.set('hello'));
console.log([obj1, obj2].map(lens.get));
See also: https://randycoulman.com/blog/2016/07/12/thinking-in-ramda-lenses/
The below two functions are almost entirely the same:
let fn = x => x.contact.name;
function fn(x) {
return x.contact.name;
}
As such, you can't use a function that returns an object property to set the object property.
Instead consider the following two functions:
let fn = (x, newVal) => {
if (newVal) x.contact.name = newVal;
return x.contact.name;
};
/* - This function included just for reference -
function fn(x, newVal) {
if (newVal) x.contact.name = newVal;
return x.contact.name;
}
*/
let myObj = { contact: { name: "Jess" } };
console.log(fn(myObj)); // name currently
fn(myObj, "John"); // set new name
console.log(myObj); // show new name on object
Maybe the following will do what you want?
I had to change the "lambda" expression a little bit, as it would not make much sense to give the return value of the original function to a potential "write" function, as it was a value and not a reference. My "new" function definition of fn works differently: it returns an array with two elements:
the "parent" object containing a particular attribute
the name of the attribute of the object.
The functions read() and write() can then pick up the return values of fn(o) and perform their particular actions accordingly.
let fn = x => [x.contact,"name"];
const read=o=>fn(o)[0][fn(o)[1]] // get the attribute as defined in fn
,write=(o,v)=>fn(o)[0][fn(o)[1]]=v; // set the attribute as defined in fn
const o={contact:{name:"Harry",country:"England"}};
console.log(read(o));
write(o,"Hermiony");
console.log(read(o));
// change the lambda function:
fn = x => [x.contact,"country"];
write(o,"Germany");
console.log(read(o));
console.log(o);
I'm trying to backwards engineer the array methods push, pull, shift, unshift, but I can't seem to figure out how to a) construct it b) call it. How could I do this? Here are the conditions:
returns an empty array object. this object should have the
following methods: push(val) adds val to the end of the array
pop() removes a value from the end and returns it unshift(val) adds
val to the beginning of the array shift() removes a value from the
beginning and returns it the goal of this problem is to reverse
engineer what array methods are actually doing and return an object
that has those methods
Here is what I initially thought it should look like.
function createArray() {
//CODE HERE
this.push = function Push(value){
if(index >= 0){
Mainarray[index++]=value;}
};
this.pop = function (){
if(index >= 0){
index--;
return Mainarray[index];
}
else{
// display message of Empty Array
console.log('Error: Item is not array');
}
};
this.unshift = function(){return ;};
}
You could use prototypes — like this:
function YourArray() {
this.arr = [];
this.index = 0;
}
YourArray.prototype.push = function( value ) {
this.arr[ this.index++ ] = value;
return this;
}
var arr = new YourArray();
arr.push('foo');
function NewArray() {
this.array = [];
}; /* Class */
NewArray.prototype.push = function(data) {
this.array.push(data);
} /* Method */
/* You should use prototypes, because all methods will became common, and if you are created methods like this.pop = function (){} then any instance will copy this functions */
var n = new NewArray();
n.push(2);
console.log(n);
Advantages of using prototype, vs defining methods straight in the constructor?
You can recreate the push method by assigning you array at position the length of the same array a value.
This is the prototype for the push:
Array.prototype.push = function(element) {
this[this.length] = element;
};
and this is for the pop method:
Array.prototype.pop = function() {
var key = this.stack.pop();
var prop = this.object[key];
delete this.object[key];
return prop;
};
You can make your own methods by changing the prototype names.
push to mypush or sthing
Example for your push function createArray:
this.push = function pushValue(value) {
this.arr[this.arr.length] = value;
};
I used native arrays methods as values assigned to keys in the returned object. The trick is to declare an array inside the object and use it as a reference. It should pass the checks you`re looking for.
function createArray() {
//CODE HERE
return {
arr: [],
push: function (val) {this.arr.push(val)},
pop: function() {return this.arr.pop()},
unshift: function (val) {return this.arr.unshift(val)},
shift: function() {return this.arr.shift()}
}
}
This works:
MyCollection.prototype.select = function (properties) {
var self = this;
return {
where: function (conditions) {
return _.chain(self.arrayOfObjects)
.where(conditions)
.map(function (result) {
return _.pick(result, properties);
})
.value();
}
};
};
It allows me to query my collection like so:
var people = collection
.select(['id', 'firstName'])
.where({lastName: 'Mars', city: 'Chicago'});
I expected to be able to write the code like this, though:
MyCollection.prototype.select = function (properties) {
var self = this;
return {
where: function (conditions) {
return _.chain(self.arrayOfObjects)
.where(conditions)
.pick(properties);
.value();
}
};
};
Lo-Dash documentation specifies the _.pick callback as "[callback] (Function|…string|string[]): The function called per iteration or property names to pick, specified as individual property names or arrays of property names." That led me to believe I could just supply the properties array, which would be applied to each item in arrayOfObjects that meets the conditions. What am I missing?
http://lodash.com/docs#pick
It expects an Object as the first parameter, you're giving it an Array.
Arguments
1. object (Object): The source object.
2. ...
3. ...
I think this is the best you can do:
MyCollection.prototype.select = function (properties) {
var self = this;
return {
where: function (conditions) {
return _.chain(self.arrayOfObjects)
.where(conditions)
.map(_.partialRight(_.pick, properties))
.value();
}
};
};
It doesn't work because _.pick expects an object, not a collection which is being passed through from the where function in your chain.
I create a class with a function like this
var Obj=function(){this.children=[];this.parent=null;}//a base class
Obj.prototype.index=function(child){
// the index of current obj
if(arguments.length==0){
return this.parent?this.parent.index(this):0;
}
// the index of a child matchs specific obj [to be override]
return -1;
}
basically it is just an overload function composed of index() and index(child).
Then I create a sub class,SubObj or whatever, inherits from Obj
SubObj.prototype.prototype=Obj;
Now, it's time to override the index(child) function,however, index() is also in the function an I don't want to overwrite it too.
One solution is to write like this
var Obj=function(){this.children=[];this.parent=null;}//a base class
Obj.prototype.index=function(child){
// the index of current obj
if(arguments.length==0){
return this.parent?this.parent.index(this):0;
}
// the index of a child matchs specific obj [to be override]
return this._index(this);
}
Obj.prototype._index=function(this){
return -1;
}
SubObj.prototype._index=function(this){/* overwriteing */}
But this will easily mislead other coders as _index(child) should be both private(should not be used except index() function) and public(is an overload function of index(),which is public)
you guys have better idea?
From my understanding, what you are trying to do should be quite doable. Although, I'd take RobG's advice and move away from trying to force a classical design onto JavaScript; JavaScript is about objects, not classes. Anyways, I digress. Here's a solution you can try out:
var Obj = function () {
this.children = [];
this.parent = null;
};
Obj.prototype.index = function (child) {
if (arguments.length === 0) {
return this.parent ? this.parent.index(this) : 0;
}
return -1;
};
var SubObj = function() {};
SubObj.prototype = new Obj();
SubObj.prototype.index = (function (base) {
var someIndex = 10;
return function (child) {
// If child is defined then we
// do our own processing.
if (child && arguments.length === 1) {
return someIndex;
}
// Otherwise we call our base/overriden version.
return base.call(this);
};
}(SubObj.prototype.index));
// Usage:
var o = new Obj(), so = new SubObj();
o.index(); // Returns 0
so.index(); // Returns 0
so.index(o); // Returns 10
There were a few issues with your prototype chain construction (SubObj.prototype.prototype = Obj doesn't actually do anything) and the definition of the index() method on the SubObj.prototype object (namely the use of this as an argument -- this will likely cause a world of pain when you try to run this in virtually any browser). I've made the fixes and implemented the override you seek (at least I think it's what you're after). Let me know if I misunderstood anything.
Do JavaScript objects/variables have some sort of unique identifier? Like Ruby has object_id. I don't mean the DOM id attribute, but rather some sort of memory address of some kind.
If you want to lookup/associate an object with a unique identifier without modifying the underlying object, you can use a WeakMap:
// Note that object must be an object or array,
// NOT a primitive value like string, number, etc.
var objIdMap=new WeakMap, objectCount = 0;
function objectId(object){
if (!objIdMap.has(object)) objIdMap.set(object,++objectCount);
return objIdMap.get(object);
}
var o1={}, o2={}, o3={a:1}, o4={a:1};
console.log( objectId(o1) ) // 1
console.log( objectId(o2) ) // 2
console.log( objectId(o1) ) // 1
console.log( objectId(o3) ) // 3
console.log( objectId(o4) ) // 4
console.log( objectId(o3) ) // 3
Using a WeakMap instead of Map ensures that the objects can still be garbage-collected.
No, objects don't have a built in identifier, though you can add one by modifying the object prototype. Here's an example of how you might do that:
(function() {
var id = 0;
function generateId() { return id++; };
Object.prototype.id = function() {
var newId = generateId();
this.id = function() { return newId; };
return newId;
};
})();
That said, in general modifying the object prototype is considered very bad practice. I would instead recommend that you manually assign an id to objects as needed or use a touch function as others have suggested.
Actually, you don't need to modify the object prototype. The following should work to 'obtain' unique ids for any object, efficiently enough.
var __next_objid=1;
function objectId(obj) {
if (obj==null) return null;
if (obj.__obj_id==null) obj.__obj_id=__next_objid++;
return obj.__obj_id;
}
I've just come across this, and thought I'd add my thoughts. As others have suggested, I'd recommend manually adding IDs, but if you really want something close to what you've described, you could use this:
var objectId = (function () {
var allObjects = [];
var f = function(obj) {
if (allObjects.indexOf(obj) === -1) {
allObjects.push(obj);
}
return allObjects.indexOf(obj);
}
f.clear = function() {
allObjects = [];
};
return f;
})();
You can get any object's ID by calling objectId(obj). Then if you want the id to be a property of the object, you can either extend the prototype:
Object.prototype.id = function () {
return objectId(this);
}
or you can manually add an ID to each object by adding a similar function as a method.
The major caveat is that this will prevent the garbage collector from destroying objects when they drop out of scope... they will never drop out of the scope of the allObjects array, so you might find memory leaks are an issue. If your set on using this method, you should do so for debugging purpose only. When needed, you can do objectId.clear() to clear the allObjects and let the GC do its job (but from that point the object ids will all be reset).
const log = console.log;
function* generateId() {
for(let i = 0; ; ++i) {
yield i;
}
}
const idGenerator = generateId();
const ObjectWithId = new Proxy(Object, {
construct(target, args) {
const instance = Reflect.construct(target, args);
instance['id'] = idGenerator.next().value;
return instance;
}
})
const myObject = new ObjectWithId({
name: '##NativeObject'
});
log(myObject.id);