Get a function from an array, javascript - javascript

Got an array with functions. I want to do a function that returns a function from the array with function name given as argument.
var arr = [
function Dog(){},
function Cat(){}
];
var getFunction = function(name){
return // should return the function with matching name
};
var dogFunction = getFunction('Dog'); // returns dog function.
https://jsfiddle.net/zcjd9pyz/
Is this possible?

if you do an associative array, it is possible
var arr = {
'dog' : function Dog(){},
'cat' : function Cat(){}
};
arr['dog']();

Functions have a name property:
var getFunction = function(name){
for (var i=0; i<arr.length; i++) {
if (arr[i].name===name) return arr[i];
}
return // return undefined
};
If you want to have a fast access, you can precompute a map by first iterating:
var map = arr.reduce(function(m,f){ m[f.name]=f; return m}, {});
which allows
var fun = map["Dog"];
Computing the map in code instead of typing it yourself lets you not repeat the name. A DRY code is easier to maintain.
EDIT: I'm not sure functions have a name on IE but I can't test it.

In ES6 you could do it without modifying the array (or in all browsers except Internet Explorer if you replace the arrow function with a normal one and use a polyfill for find:
var getFunction = function(name){
return arr.find( func => name === func.name );
};
Even in ES6 though, I don't see a good reason to do that. I think you should follow Deblaton Jean-Philippe's answer and change the array to an object, mapping the names to the functions.

You can use this sample work around of mine, instead of matching for string you can use it based on function name
https://gist.github.com/freewayz/56bd9db6d4164a42be75
var myArray = [{"name" : "pitaside", "id" : 1}, {"name":"github", "id" : 3}]
filterArrayByType: function (arrayToMatch, fieldType, matcher) {
if(! arrayToMatch instanceof Array){throw ("Not an Array")}
var filterTypeToReturn = arrayToMatch.filter((items) => {
var temp;
if (items[String(fieldType)] === matcher) {
temp = items[String(fieldType)]
}
return temp;
}
);
return filterTypeToReturn;
}
var myMatcher = 'github'
var id3 = filterArrayByType(myArray, 'name', myMatcher)[0].id
//returns 3

You can use Function.prototype.toString(). Unlike name it is supported by most of the modern browsers as well as by Node.js.
var arr = [
function Dog ( ) {},
function Cat ( ) {}
];
var getFunction = function(name){
'use strict';
// could use find but it isn't supported by IE
return arr.filter(function (func) {
return /^function\s+(\w+)/.exec(func.toString())[1] === name;
})[0];
};
console.log(getFunction('Dog'));
console.log(getFunction('Cat'));
console.log(getFunction('Unknown'));

Related

How does angular or js return data without specifying a parameter name

The title doesnt really explain what i am trying to ask here. Well here is an example:
.factory('Story', function($http) {
var storyFactory = {};
Factory.getStory = function() {
return $http.get('/api');
}
})
.controller('StoryController', function(story) {
var vm = this;
Story.allStory()
.sucess(function(mydata) {
})
})
So how allStory() returns data into mydata?
Are you asking about the way javascript replaces the written code with the logical object at the time?
eg.
console.log(new Array({"getWalkDetails":function(){return {"MaxSpeed":15, "DistanceWalked": 123}} },
"walking on sunshine",
"oh oh" ).shift().getWalkDetails().MaxSpeed);
//outputs "15" to the console
This can be rewritten as
var arr = new Array();
var func = function(){
var details = new Object();
details.MaxSpeed =15;
details.DistanceWalked = 124;
return details;
}
var obj = {"getWalkDetails" : func};
arr.push(obj);
arr.push("walking on sunshine");
arr.push("oh oh");
var firstItem = arr.shift();
//the array function 'shift()' is used to remove the first item in the array and return it to the variable
var walkingDetails = firstItem.getWalkingDetails()//same as func() or obj.getWalkingDetails()
console.log(walkingDetails.MaxSpeed);//15
As you can see we stored most of the the interpreted outputs as variables to be used seperately.
EDIT:
If you are asking how to pass objects by reference in javascript to allow the mydata variable to receive any changes done to it in the function it is passed to. then this question might be helpful to you:
javascript pass object as reference
EDIT:
edited the code above a bit more
I'm not entirely sure either what you mean (especially because I never worked with Angular), but I have a feeling you're puzzled by this little trick:
//+ fn -> [a]
var getParameterNames = (function () {
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,
ARGUMENT_NAMES = /([^\s,]+)/g;
return function (fn) {
var fnStr = fn.toString().replace(STRIP_COMMENTS, ''),
names = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
return names || [];
};
})();
function add (x, y) {
return x + y;
}
console.log(getParameterNames(add)); // => ['x', 'y']
Edit: And here's a jsfiddle.

_.function() version of underscore's _.property() function

Underscore has an Object function _.property(key) which returns a function that itself returns the 'key' property of any passed in object. For example:
var moe = {name: 'moe'};
var propFunction = _.property('name');
var value = propFunction(moe);
=> 'moe'
I'm wondering if there's a good way with Underscore to get the same behavior with an object's function in addition to just an object's property. I'm pretty sure there's not a single function for it, but I'm wondering if there's some reasonable combination of functions that together will do what I want. For example:
var moe = {getName: function() { return 'moe'; }};
var funcFunction = _.underscoreGoodnessHere('getName');
var value = funcFunction(moe);
=> 'moe'
This would be to remove some of the boilerplate in some pseudo-real code I have like this:
this.collection.filter(function(model) { return model.isHidden(); });
// could change to this:
this.collection.filter(_.underscoreGoodness('isHidden'));
For what it's worth, if there is not a great way to do what I've asked but you still have a better way to write my pseudo-real code above, I'd still love to hear that!
You're looking for the callback function that Underscore uses in _.invoke() - however that is not public. You can build it easily yourself:
_.method = function(name) {
var args = _.tail(arguments),
isFunc = _.isFunction(name);
return function(value) {
return (isFunc ? name : value[name]).apply(value, args);
};
};
You can create your own function which is based on _.property:
function callProperty(prop) {
var getProperty = _.property(prop);
return function(obj) {
return getProperty(obj)();
};
}
Shortest I can think of is using: _.result()
function functionProp(prop){
return function(obj){
return _.result(obj, prop);
};
}
Which will actually also work for non-function properties. So:
var moe = {getName: function() { return this.name; }, name: 'moe'};
'moe' == functionProp('getName')(moe);
'moe' == functionProp('name')(moe);
Underscore's property function doesn't distinguish between "functional properties" and "regular properties". To it, a function object stored under a property name is the property value itself. Extending the example you provided you see the following:
var moe = {getName: function() { return 'moe'; }};
var funcFunction = _.property('getName');
var getter = funcFunction(moe);
=> function() { return 'moe'; }
var value = getter();
=> 'moe'
funcFunction(moe)();
=> 'moe'
If you want to build your own, you can do something like the following:
function functionalProperty(name) {
var getter = _.property(name);
return function (o) { return getter(o); };
}
Or, if you want to support both types of properties with the same function, you could do this
function anyProperty(name) {
var getter = _.property(name);
return function (o) {
var val = getter(o);
return _.isFunction(val) ? val() : val;
};
}
Just be careful that you only use this for actual properties and not functions that you didn't really want to get invoked.

stringify javascript function

I am in the final stages of a game development and i have a bunch of objects like this;
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
What I want to do now is place all of my game objects into an array;
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
The plan is to then save the savedGameObjects array and then recall it when the user loads the game again.
If I replace savedGameObjects['bedroom'] = bedroom; with savedGameObjects['bed'] = 'slappy'; it works but not when I have the object.
I really need to save the objects in their current state. I'd rather not go through each object saving key pieces of information one by one.
This feels like a bit of a hack, but its the best I can come up with right now
Your serialization/deserializtion utility
This is going to attach obj.constructor.name to obj.__prototype before serialization. Upon deserializing, the prototype will be put back in place.
(function(global) {
function serialize(obj) {
obj.__prototype = obj.constructor.name;
return JSON.stringify(obj);
};
function deserialize(json) {
var obj = JSON.parse(json);
obj.__proto__ = global[obj.__prototype].prototype;
return obj;
}
global.serialize = serialize;
global.deserialize = deserialize;
})(window);
A sample "class"
(function(global) {
function Foo() {
this.a = "a";
this.b = "b";
}
Foo.prototype.hello = function() {
console.log("hello");
}
global.Foo = Foo;
})(window);
Let's try it out
var foo = new Foo();
var json = serialize(foo);
console.log(json);
var newFoo = deserialize(json);
console.log('a', newFoo.a); // a
console.log('b', newFoo.b); // b
newFoo.hello(); // hello
Watch out for some gotchas
If you use an expression to define your "class", you will have a nameless constructor
var Foo = function() {};
var foo = new Foo();
foo.constructor.name; // ""
As opposed to a named function
function Foo() {}
var foo = new Foo();
foo.constructor.name; // Foo
In order for serialize and deserialize to work, you will need to use named functions
Another gotcha
The deserialize method expects your "classes" to exist on the in the same namespace (window in this case). You could encapsulate your game object classes in another way, just make sure that you reconfigure the deserialize method so that it can find the prototypes as needed.
Making this better
Instead of attaching serialize to the global window, you could have serialize live on (e.g.) the GameObject.prototype then your individual classes could inherit from GameObject. Serializing an object would then be as simple as
var json = foo.serialize();
// {"a":"a","b":"b","__prototype":"Foo"}
You could then define deserialize as GameObject.deserialize and restoring foo would be
var foo = GameObject.deserialize(json);
An alternative solution
Instead of implementing a custom serializer and deserializer, you could make very clever use of the Factory Method Pattern.
This might be a little verbose, but it does give you individual control over how a game object should be deserialized/restored.
var savedData = // your normal JSON here
var player = Player.create(savedData.player);
var items = [];
for (var i=0, i<savedData.items.length; i++) {
items.push(Item.create(savedData.items[i]));
}
var map = Map.create(savedData.map);
This was a pretty interesting problem and I'm sure you're not the first to encounter it. I'm really curious to see what other people come up with.
If I run the following code in a browser there is no problem getting the JSON string of the bedroom object, not sure what the problem is.
Note that JSON is data and bedroom is an object, bedroom may have behaviour like turnOffLight() that JSON doesn't have.
roomBedroom = function () {
this.title = "Bedroom";
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
}
var bedroom = new roomBedroom();
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
//logs {"bedroom":{"title":"Bedroom","description":
// "I'm in abedroom","noun":"bed","entities":[]}}
console.log(JSON.stringify(savedGameObjects));
So if you want to re create object instances from JSON data then you can change your constructor:
roomBedroom = function (args) {
//following fails fast and loud, you could silently
//fail by setting args to {}
if(typeof args!=="object")
throw new Error("Have to create roomBedroom by passing an object");
//or do args={} to silently fail
this.title = args.title||"Bedroom";
this.description = args.description||"I'm in a bedroom";
this.noun = args.noun||"bed";
//if entities are objects with behavior
// you have to re create them here passing the JSON data
// as I've done with roomBedroom
this.entities = args.entities||new Array();
}
var jsonString='{"bedroom":{"title":"Bedroom",'+
'"description":"I\'m in a bedroom",'+
'"noun":"bed","entities":[]}}';
var bedroom = new roomBedroom({});
bedroom.entities.push({hi:"there"});
bedroom.title="Master Bedroom";
//serialize bedroom to a json string
var jsonString = JSON.stringify(bedroom);
//create a roomBedroom instance named br2 using
// the serialized string
var br2=new roomBedroom(JSON.parse(jsonString));
//compare if they are the same
console.log(JSON.stringify(bedroom)===JSON.stringify(br2));//true
I have an approach that might work for you. You can see it in action on JSFiddle.
The main point is to use the reviver parameter to JSON.parse to reconstruct your object when it's parsed.
I do this with a general-purpose reviver that can be configured for multiple different types, although here the only one used is the RoomBedroom constructor. This implementation assumes that you have simple copy constructors that create new objects using a reference to an existing one. (For other, more sophisticated possibilities, see an answer to another question I gave in February.) To make it easy to have a copy constructor, I have one more function that accepts a very simple constructor function and a set of default values and builds a copy constructor function for you.
var MultiReviver = function(types) {
return function(key, value) {
var type;
for (var i = 0; i < types.length; i++) {
type = types[i];
if (type.test(value)) {
return new type.constructor(value);
}
}
return value;
};
};
var makeCloningConstructor = (function() {
var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
var F = function() {};
return function(Constructor, defaults) {
var fn = function(obj) {
Constructor.call(this);
var self = this;
var config = obj || {};
Object.keys(defaults).forEach(function(key) {
self[key] = clone(defaults[key]);
});
Object.keys(config).forEach(function(key) {
self[key] = clone(config[key]);
});
};
F.prototype = Constructor.prototype;
fn.prototype = new F();
fn.constructor = Constructor;
return fn;
};
})();
// Note: capitalize constructor functions
var RoomBedroom = makeCloningConstructor(function RoomBedroom() {}, {
title: "Bedroom",
description: "I'm in a bedroom",
noun: "bed",
entities: [] // Note: use `[]` instead of `new Array()`.
});
RoomBedroom.prototype.toggleLight = function() {
this.lightOn = !this.lightOn;
};
RoomBedroom.prototype.checkLights = function() {
return "light is " + (this.lightOn ? "on" : "off");
};
var bedroom = new RoomBedroom();
bedroom.windowCount = 3; // add new property
bedroom.noun = "king-sized bed"; // adjust property
bedroom.toggleLight(); // create new propery, use prototype function
console.log(bedroom.checkLights());
var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
var reviver = new MultiReviver([{
constructor: RoomBedroom,
test: function(obj) {
var toString = Object.prototype.toString, str = "[object String]",
arr = "[object Array]";
return toString.call(obj.title) == str &&
toString.call(obj.description) == str &&
toString.call(obj.noun) == str &&
toString.call(obj.entities) == arr;
}
}]);
var retrievedGameObjects = JSON.parse(jsonGame, reviver);
// data comes back intact
console.log(JSON.stringify(retrievedGameObjects, null, 4));
// constructor is as expected
console.log("Constructor: " + retrievedGameObjects.bedroom.constructor.name);
// prototype functions work
console.log(retrievedGameObjects.bedroom.checkLights());
I don't know if it's precisely what you were looking for, but I think it's at least an interesting approach.
the faster route
It is better — from an optimisation point of view — to do as Adeneo states, which is power each of your Game Objects by an exportable simple object i.e:
roomBedroom = function(){
this.data = {};
this.data.title = 'Bedroom'
/// and so on...
}
These can then be easily stored and re-imported just by JSON.Stringifying and overwriting the data property. For example, you could set-up the system that Maček mentions (+1) which is to give each of your game objects serialize and deserialize functions:
roomBedroom.prototype.serialize = function(){
return JSON.stringify( this.data );
};
roomBedroom.prototype.deserialize = function( jstr ){
this.data = JSON.parse(jstr);
};
the quicker way
However, you can make a simple addition to what you already have using the following:
First enhance your Game Objects with an objectName property. This is because constructor.name and function.name are unreliable and do strange things the further back in time you go, far better to use a string you have set in stone.
var roomBedroom = function ( title ) {
this.objectName = "roomBedroom";
this.title = title;
this.description = "I'm in a bedroom";
this.noun = "bed";
this.entities = new Array();
};
Then the additional code to help with storage:
var storage = {};
/// add your supported constructors to this list, there are more programmatic
/// ways to get at the constructor but it's better to be explicit.
storage.constructors = {
'roomBedroom' : roomBedroom
};
/// take an instance and convert to simple object
storage.to = function( obj ){
if ( obj.toStorage ) {
return obj.toStorage();
}
else {
var keep = {};
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) && !obj[i].call ) {
keep[i] = obj[i];
}
}
return keep;
}
}
/// take simple object and convert to an instance of constructor
storage.from = function( obj ){
var n = obj && obj.objectName, c = storage.constructors[n];
if ( n && c ) {
if ( c.fromStorage ) {
return c.fromStorage( obj );
}
else {
var inst = new c();
for ( var i in obj ) {
if ( obj.hasOwnProperty(i) ) {
inst[i] = obj[i];
}
}
return inst;
}
}
else {
throw new Error('`' + n + '` undefined as storage constructor');
}
}
Once you have that you can use it like so:
var savedGameObjects = {};
savedGameObjects['bedroom'] = storage.to(new roomBedroom("bedroom"));
savedGameObjects['bedroom2'] = storage.to(new roomBedroom("bedroom2"));
var jsonGame = JSON.stringify(savedGameObjects);
console.log(jsonGame);
savedGameObjects = JSON.parse(jsonGame);
for( var i in savedGameObjects ) {
savedGameObjects[i] = storage.from(savedGameObjects[i]);
console.log(savedGameObjects[i]);
}
extras
You can also be specific about the way objects get stored/unstored by supplying toStorage and fromStorage methods on your constructed instances and constructors respectively. For example, you could use the following if you only wanted to store titles of roomBedrooms. Obviously this is an unrealistic use-case, you'd more often use this to avoid storing cached or computed sub-objects and properties.
roomBedroom.prototype.toStorage = function( obj ){
var ret = {};
ret.title = obj.title;
return ret;
};
roomBedroom.fromStorage = function( obj ){
var inst = new roomBedroom();
inst.title = obj.title;
return inst;
};
The above also means you can take advantage of improving your Game Object construction by providing parameters, rather than iterating over properties which can be slow and error-prone.
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj.title );
};
Or even:
roomBedroom.fromStorage = function( obj ){
return new roomBedroom( obj ); // <-- the constructor processes the import.
};
fiddle
http://jsfiddle.net/XTUdp/
disclaimer
The above code relies on the existence of hasOwnProperty which is not present cross-browser yet, a polyfill should be used until it is... or, if you aren't doing anything complicated with prototype inheritance you don't need to worry and can remove it from the code.
you can declare a big variable like
var world = {};
and each small variable declare as
var bedroom = world.bed = (world.bed || new roomBedroom());
remember never change bedroom to another object, i think this will work fine, but looks too long winded

Unexpected behavior using getters and setters

Look this code:
<script>
function dbg (object) {
var _string = "";
for (var a in object) {
_string += a + ":\n";
for (var b in object[a])
if (/^get_/.test (b))
_string += "\t" + b + " - " + object[a][b] () + "\n";
}
return _string;
}
function Order () {
var products = [];
this.get_products = function () {return products;}
this.set_products = function (_products) {products = _products;}
}
function Product () {
var id = null;
var name = null;
this.get_id = function () {return id;}
this.get_name = function () {return name;}
this.set_id = function (_id) {id = _id;}
this.set_name = function (_name) {name = _name}
}
var order = new Order ();
var product = new Product ();
product.set_id (1);
product.set_name ("Banana");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Ok
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
alert (dbg (order.get_products ())); // Duplicated values! What?
</script>
The first time you push the object "Product" into the object "Order", everything looks fine.
When you set new values to the object "Product", the object itself overwrites the previous values of the object "Order". The final result is a array of duplicated values. Is it normal ? Is there a workaround? Just tried everything I knew without success. Thanks.
Crazy Train has already answered it in the comments. The question is listed having 0 answers so I'll add it as an answer.
When adding a variable containing an object to an array you add a reference to the variable, when you re assign the variable the reference is broken.
Adding a variable containing an object to an array then re assigning the variable doesn't change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object=33;
console.log(arr);//=[Object {name="john"}]
Adding a variable containing an object to an array then changing the internal values of the object that the variable contains does change the object in the array:
var arr=[];
var object={name:"John"};
arr.push(object);
object.name="Jane";
console.log(arr);//=[Object {name="Jane"}]
So to correct your code you could do the following:
Create a new variable for the product to be added:
var product2=new Product();
product2.set_id (2);
product2.set_name ("Orange");
order.set_products (order.get_products ().concat (product2));
Or break the reference between your product variable and the products array in order:
product=null;//product has no ref to order.products
product=new Product();
product.set_id (2);
product.set_name ("Orange");
order.set_products (order.get_products ().concat (product));
I would not define members of an object in a constructor function with var as JavaScript doesn't support private members. You can simulate them by creating closures but that has it's own problem when you have instance specific privates (as is your case). You can't use prototype if the functions need to access private instance variables, you can't clone it unless you have public accesssors, inheritance and overriding functions will be a pain.
Here is some more info on using constructor functions.
If you have Chrome or Firefox (with Firebug) then you can press F12 to open the console. You an detach the console window (have it's own window) then copy code in the before mentioned answers and paste them in the commandline of the console. There you can run and re run the code, change and see the output to better understand JS behavior.
You are just overriding the variables in object. I'd do it like this, much simpler:
var products = {
set : function(name,id) {
products.list.push({name:name,id:id});
},
get : function(id) {
var r;
if(typeof id === 'number'){
products.list.forEach(function(e,i){ if(e.id==id) r= products.list[i];});
} else {
products.list.forEach(function(e,i){ if(e.name==id) r = products.list[i];});
}
return r;
},
list : []
};
var order={
set : function(p) {
order.list[p.id]=p;
},
get : function(id) {
return order.list[id];
},
delete : function(id) {
return delete order.list[id];
},
list : {}
};
then you can do this
products.set('apple',34);
products.set('orange',4);
products.set('mango',1);
var x = products.get(1);
var y = products.get('orange');
order.set(x);
order.set(y);
working demo:
http://jsfiddle.net/techsin/tjDVv/2/

Return a dynamically generated function without eval()

I'm trying to dynamically create function when iterating over an array and
I need the arguments in the array to be set according to the value of the current index.
For example:
var array = ['apple','orange','banana'];
I need to have these three functions:
function() { return 'apple' };
function() { return 'orange' };
function() { return 'banana' };
I tried to return a constructed function from an external one but the expression in it won't evaluate and I end up with three of these:
function() { return array[i] };
Is there a way to dynamically create such a function without using eval()?
You can create the functions like so:
var funcs = {};
for (var i=0;i<array.length;i++)
{
funcs[array[i]] = (function(val)
{
return function()
{
return val;
};
}(array[i]));
}
which can be called like so:
funcs.apple();// returns "apple"
But also, depending on the value of some var:
var someVar = 'banana';
if (funcs.hasOwnProperty(someVar))
{
funcs[someVar]();
}
If what you're after is a single (possibly global) function, that depending on, for example, the URI, you just have to write this:
var myFunc = (function()
{
var retVal = location.mathname.match(/^\/([^\/]+)/)[1];
return function()
{
return retVal;
};
}());
Note that the function won't be hoisted, as it is an expression.
I've written a lot about IIFE's (Immediatly Invoked Function Expressions), how they work and why, so best check my answer here if you don't fully understand these code snippets. It's quite easy once you get the logic, and you'll soon find yourself writing code like this all the time... tey really are incredibly powerful things, closures are!
This is what I would do:
function constant(value) {
return function () {
return value;
};
}
var array = ["apple", "orange", "banana"];
var fruit = array.map(constant);
alert(fruit[0]()); // apple
alert(fruit[1]()); // orange
alert(fruit[2]()); // banana
Simple. See the demo: http://jsfiddle.net/tfS2F/
You can also use the initial array as a key as follows:
alert(fruit[array.indexOf("orange")]()); // orange
See the demo: http://jsfiddle.net/tfS2F/1/
This one will not work, i leave it to illustrate the infamous loop problem:
The best way to achieve this would be to create a context var before creating the function, hope this illustrates it http://jsfiddle.net/jkymq/
var array = ['apple','orange','banana'];
var methods = {}
for (pos = 0 ; pos < array.length; pos ++){
var target = array[pos];
var newMethod = function(){alert (target);}
methods[target] = newMethod;
}
for (foo in methods){
methods[foo]();
}

Categories

Resources