can i search an array of functions and run the searched function? - javascript

I am trying to write an application that searches an array of functions and does the function that was found.
var search = 'test';
var valid_input_array = [
function test(){console.log('test');},
function abc(){console.log('abc');}
];
for (i = 0; i < valid_input_array.length; i++) {
if (valid_input_array[i].name === search) {
valid_input_array.search;
}
}
It does find the function but it doesn't run it.

You need to call found function
var search = 'test';
var valid_input_array = [function test(){console.log('test');},function abc(){console.log('abc');}];
for(i = 0; i < valid_input_array.length; i++){
if(valid_input_array[i].name === search){
valid_input_array[i]()
}
}

You could also use Array#find()
var search = 'test';
var valid_input_array = [
function test(){console.log('test');},
function abc(){console.log('abc');}
];
var funcWanted = valid_input_array.find(function(fn) {
return fn.name === search
})
// only call it if find doesn't return false
funcWanted && funcWanted()

Related

Extend Javascript Syntax to Add Typing

I'd like to extend javascript to add custom type checking.
e.g.
function test(welcome:string, num:integer:non-zero) {
console.log(welcome + num)
}
which would compile into:
function test(welcome, num) {
if(Object.prototype.toString.call(welcome) !== "[object String]") {
throw new Error('welcome must be a string')
}
if (!Number.isInteger(num)) {
throw new Error('num must be an integer')
}
console.log(welcome + num)
}
What's the most straightforward way of doing this?
So far i've looked at:
sweet.js (online documentation looks out of date as I think it's going through some sort of internal rewrite)
esprima and escodegen (not sure where to start)
manually parsing using regular expressons
After evaluating all the various options, using sweet.js appears to be the best solution. It's still fairly difficult to get working (and I am probably doing stuff the wrong way) but just in case someone want's to do something similar this here was my solution.
'use strict'
syntax function = function(ctx) {
let funcName = ctx.next().value;
let funcParams = ctx.next().value;
let funcBody = ctx.next().value;
//produce the normal params array
var normalParams = produceNormalParams(funcParams)
//produce the checks
var paramChecks = produceParamChecks(funcParams)
//produce the original funcBody code
//put them together as the final result
var params = ctx.contextify(funcParams)
var paramsArray = []
for (let stx of params) {
paramsArray.push(stx)
}
var inner = #``
var innerStuff = ctx.contextify(funcBody)
for (let item of innerStuff) {
inner = inner.concat(#`${item}`)
}
var result = #`function ${funcName} ${normalParams} {
${paramChecks}
${inner}
}`
return result
function extractParamsAndParamChecks(paramsToken) {
var paramsContext = ctx.contextify(paramsToken)
//extracts the actual parameters
var paramsArray = []
var i = 0;
var firstItembyComma = true
for (let paramItem of paramsContext) {
if (firstItembyComma) {
paramsArray.push({
param: paramItem,
checks: []
})
firstItembyComma = false
}
if (paramItem.value.token.value === ',') {
firstItembyComma = true
i++
} else {
paramsArray[i].checks.push(paramItem.value.token.value)
}
}
for (var i = 0; i < paramsArray.length; i++) {
var checks = paramsArray[i].checks.join('').split(':')
checks.splice(0, 1)
paramsArray[i].checks = checks
}
return paramsArray
}
function produceNormalParams(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
//Produces the final params #string
var inner = #``
var first = true
for (let item of paramsArray) {
if (first === true) {
inner = inner.concat(#`${item.param}`)
} else {
inner = inner.concat(#`,${item.param}`)
}
}
return #`(${inner})`
}
function produceParamChecks(paramsToken) {
var paramsArray = extractParamsAndParamChecks(paramsToken)
var result = #``
for (let paramObject of paramsArray) {
var tests = produceChecks(paramObject)
result = result.concat(#`${tests}`)
}
return result
}
function produceChecks(paramObject) {
var paramToken = paramObject.param
var itemType = paramObject.checks[0]
var checks = paramObject.checks
if (itemType === undefined) return #``
if (itemType === 'array') {
return #`if (Object.prototype.toString.call(${paramToken}) !== "[object Array]") throw new Error('Must be array:' + ${paramToken})`
else {
throw new Error('item type not recognised: ' + itemType)
}
}
}

Binding event listeners multiple times in JavaScript

I'm trying to get the directory list, level by level, using JavaScript.
I have this paths array as input.
var _paths = [];
_paths.push("meta1/meta2/test/home/myself/hi.jpg");
_paths.push("meta1/meta2/test/home/myself/hi1.jpg");
_paths.push("meta1/meta2/test/home/myself/hi2.jpg");
_paths.push("meta1/meta2/test/work/she/100.jpg");
_paths.push("meta1/meta2/test/work/she/110.jpg");
_paths.push("meta1/meta2/test/work/she/120.jpg");
_paths.push("meta1/meta2/test/work/hard/soft/she/120.jpg");
_paths.push("meta1/meta2/test/work/hard/soft/she/121.jpg");
_paths.push("meta1/meta2/test/work/she/220.jpg");
and I want to have a "test" as output, which will be clickable. After click "test", it should be replaced by "home" and "work". After click on "home" - "myself", on "work" - "hard" and "she".
I wrote this:
CodepenCode
and it works only once, only when clicking on "test".
Simply rebind the listeners after the directories have been drawn. You bind them only once, thus they work only once.
Wrap the binding function into a named function:
function bindListeners(){
$('.sub').click(function() {
word = $(this).text();
filteredArr = findString(_paths, word);
drawList(filteredArr, word);
});
}
And call it at the end of drawList:
var drawList = function (paths, word) {
var folders = getFolders(paths, word);
if (folders.length > 0) {
$('.canvas').html('');
for (i = 0; i < folders.length; i++) {
$('.canvas').append("<div class='sub'>" + folders[i] + "</div><br />");
}
}
bindListeners();
}
Demo.
If anyone is curious about building out the data structure:
(function iteratePaths() {
var dirs = [];
for(var i = 0; i < _paths.length; i++) {
buildDirectories(dirs, _paths[i].split('/'));
}
})();
function findDir(dir, obj) {
for(var i = 0; i < dir.length; i++) {
if(dir[i].name === obj.name) {
return dir[i];
}
}
return undefined;
}
function buildDirectories(dir, subs) {
if(subs.length === 0) return;
var obj = {name: subs.shift(), dirs: []};
var existingDir = findDir(dir, obj);
if(!existingDir) {
dir.push(obj);
}
buildDirectories((existingDir || obj).dirs, subs);
}

JavaScript - reactivity

I have this code. It does that always when there is function, that is interested on some key in Session, it will be called whenever the key changed its value.
The problem is that to track what keys the function is interested, i need to run that function once and it can have some sideffects (if that function will manipulate DOM for instance). How can i run that function without affecting the current environment, if it is possible....?
var checkRunning = false;
var keys = [];
var checks = {};
var Session = {
get: function (key) {
if (checkRunning) {
keys.push(key);
}
},
set: function (key, value) {
if (checks[key]) {
var l = checks[key].lenght;
for (var i = 0; i < l; i++) {
checks[key][i]();
}
}
}
};
function check(f) {
checkRunning = true;
f();
checkRunning = false;
var l = keys.lenght;
for (var i = 0; i < l; i++) {
if (checks[keys[i]]) {
checks[keys[i]].push(f);
}
else {
checks[keys[i]] = [f];
}
}
keys = [];
}
//how to use
var a = "something";
check(function () {
// this function should be run always when Session key "a_dep" will change
a = Session.get("a_dep");
});
Session.set("a_dep", 10);
Session.set("a_dep", 20);
So I refactored your code to achieve what I understood you wanted.
I first redefined Session and its getter/setter. I store the values in a literal object with the key to access it :
function Session () {
this.myDictionnary = {};
this.myCallbacks = {};
}
Session.prototype.set = function (key, value) {
this.myDictionnary[key] = value;
}
Session.prototype.get = function (key) {
return this.myDictionnary[key];
}
Here we have our core code. Now you want to call a function every time you set a value to a key, so we have to
Set a function (or function set) - key couple
Call this function (or these functions) when a value is set to key
Note the myCallbacks literal object which will handle these couples. So first, create the key - function couple :
Session.prototype.callbackWhenSet = function (key, callback) {
if(!this.myCallbacks[key]) {
this.myCallbacks[key] = [];
}
this.myCallbacks[key].push(callback);
}
Then call it when set is called (here I rewrite the set function )
Session.prototype.set = function (key, value) {
this.myDictionnary[key] = value;
if(this.myCallbacks[key]) {
for(var i = 0; i < this.myCallbacks[key].length; i++) {
this.myCallbacks[key][i]();
}
}
}
Finally we can test it !
var test = new Session();
test.callbackWhenSet("a_dep", function () {
alert("a_dep is set to " + test.get("a_dep"));
});
test.set("a_dep", 10);
test.set("something", 250);
test.set("a_dep", 20);
As you can see here, we have an alert showed each time "a_dep" is set.
Or you can use ProAct.js and do stuff like this:
var obj = ProAct.prob({
a: 4
});
obj.p('a').on(function () {
console.log('obj.a has changed!');
});

Getting nested obj value

Given the following obj:
var inputMapping = {
nonNestedItem: "someItem here",
sections: {
general: "Some general section information"
}
};
I'm writing a function to get that data by passing in a string "nonNestedItem" or in the nested case "sections.general". I'm having to use an eval and I was wondering if there was maybe a better way to do this.
Here is what I have so far and it works okay. But improve!
function getNode(name) {
var n = name.split(".");
if (n.length === 1) {
n = name[0];
} else {
var isValid = true,
evalStr = 'inputMapping';
for (var i=0;i<n.length;i++) {
evalStr += '["'+ n[i] +'"]';
if (eval(evalStr) === undefined) {
isValid = false;
break;
}
}
if (isValid) {
// Do something like return the value
}
}
}
Linky to Jsbin
You can use Array.prototype.reduce function like this
var accessString = "sections.general";
console.log(accessString.split(".").reduce(function(previous, current) {
return previous[current];
}, inputMapping));
Output
Some general section information
If your environment doesn't support reduce, you can use this recursive version
function getNestedItem(currentObject, listOfKeys) {
if (listOfKeys.length === 0 || !currentObject) {
return currentObject;
}
return getNestedItem(currentObject[listOfKeys[0]], listOfKeys.slice(1));
}
console.log(getNestedItem(inputMapping, "sections.general".split(".")));
You don't need to use eval() here. You can just use [] to get values from an object. Use a temp object to hold the current value, then update it each time you need the next key.
function getNode(mapping, name) {
var n = name.split(".");
if (n.length === 1) {
return mapping[name];
} else {
var tmp = mapping;
for (var i = 0; i < n.length; i++) {
tmp = tmp[n[i]];
}
return tmp;
}
}

JavaScript: Having trouble returning custom object information

I've made some new objects with object methods and I'm having trouble returning the information.
I intend for allPages to be a 2d array:
var allPages = [[]];
function textbox(type)
{
this.type=type;
this.getInfo = function () { return ( this.type ); };
}
function addTextbox(dropdown)
{
var myindex = dropdown.selectedIndex;
var SelValue = dropdown.options[myindex].value;
if(SelValue == "String")
{
var tb = new textbox("string");
allPages[allPages.length-1].push(tb);
var string = "";
for (i = 0;i < allPages.length;i++)
{
for(j = 0;j < allPages[i].length;j++)
{
string = string + allPages[i][j].getInfo;
}
}
<!-- Problem here: prints "function () { return this.type; }"-->
document.write(string);
}
}
}
You are not calling the function, you are referencing it
allPages[i][j].getInfo;
should be
allPages[i][j].getInfo();
3 lines above where you state the problems exists, it should be:
string = string + allPages[i][j].getInfo(); // mind the () at the end.

Categories