No jQuery please!
The Web says that the native String.concat() and join() functions of JS are to be avoided because of their poor performance, and a simple for() loop of += assignments should work a lot faster.
So I'm trying to create a function in pure JavaScript that will concatenate strings. This is somewhat how I envision it:
I want a main function concatenate() that will concatenate all passed arguments and additionally insert a variable string after each concatenated argument, except for the last one.
If the main function is called by itself and without the chained .using() function, then that variable string should be an empty one, which means no separators in the result.
I want a chained sub-function .using() that will tell the main concatenate() function what certain string other than the default '' empty string to add after each concatenated segment.
In theory, it should work like this:
concatenate('a','b','c'); /* result: 'abc' */
concatenate('a','b','c').using('-'); /* result: 'a-b-c' */
I want to avoid having two separate functions, like concatenate() and concatenateUsing(), because the concatenateUsing() variant would then have to utilize a special constant argument (like arguments[0] or arguments[arguments.length-1]) as the injected separator and that would be terribly untidy. Plus, I would always forget which one it was.
I also want to avoid having a superceding Concatenate object with two separate sub-methods, like Concatenate.strings() and Concatenate.using() or similar.
Here are some of my failed attempts so far...
Attempt #1:
function concatenate()
{
var result="";
if(this.separator===undefined){var separator=false;}
for(var i=0; i<arguments.length; i++)
{result += arguments[i] + ((separator && (i<arguments.length-1))?separator:'');}
this.using=function(x)
{
this.separator=x;
return this;
}
return result;
}
So what I'm trying to do is:
check if the separator variable is undefined, this means it wasn't set from a sub-method yet.
If it's undefined, declare it with the value false for later evaluation.
Run the concatenation, and if separator has another value than false then use it in each concatenation step - as long as it's not the last iteration.
Then return the result.
The sub-method .using(x) should somewhere along the way set the
value of the separator variable.
Naturally, this doesn't work.
Attempt #2:
var concatenate = function()
{
var result="";
var separator="";
for(var i=0; i<arguments.length; i++)
{result += arguments[i] + ((separator && (i<arguments.length-1))?separator:'');}
return result;
}
concatenate.prototype.using=function(x)
{
this.separator=x;
return this;
}
It also doesn't work, I assume that when this is returned from the using() sub-method, the var separator="" of the main concatenate() function just overwrites the value with "" again.
I tried doing this 4 or 5 different ways now, but I don't want to bore you with all the others as well.
Does anyone know a solution for this puzzle?
Thanks a lot in advance!
What you are trying to do is impossible.
You cannot chain something to a method call that returns a primitive, because primitives do not have (custom) methods1.
And you cannot make the first function return different things depending on whether something is chained or not, because it doesn't know about its call context and has to return the result before the method call is evaluated.
Your best bet is to return an object that can be stringified using a custom toString method, and also offers that using thing. It would be something along the lines of
function concatenate() {
return {
args: Array.from(arguments), // ES6 for simplicity
using: function(separator) {
return this.args.join(separator);
},
toString: function() {
return this.args.join("");
}
};
}
console.log(String(concatenate('a','b','c')); // result: 'abc'
// alternatively, use ""+… or explicitly call the ….toString() method
console.log(concatenate('a','b','c').using('-')); // result: 'a-b-c'
1: No, you don't want to know workarounds.
I want to search a string in a nested JSON object. If the string found in an object, I need to return that object.
I am using a recursive function to achieve this. The problem is, the function is recursing until the end and not returning the object found.
Please see the entire code in jsfiddle
function search(obj, name) {
console.log(obj["name"], ",", name, obj["name"] == name);
if (obj["name"] == name) {
return obj; //NOT RETURNING HERE
}
if (obj.children || obj._children) {
var ch = obj.children || obj._children;
//console.log(ch);
ch.forEach(function(val) {
search(val, name)
});
}
return -1;
}
search(myJson, "VM10-Proc4")
I am not sure what is going wrong.
You need to stop looping over children when it finds one that matches.
function search(obj, name) {
console.log(obj.name, ",", name, obj.name == name);
if (obj.name == name) {
return obj;
}
if (obj.children || obj._children) {
var ch = obj.children || obj._children;
for (var i = 0; i < ch.length; i++) {
var found = search(ch[i], name);
if (found) {
return found;
}
}
}
return false;
}
FIDDLE demo
The correct return value is getting lost in the chain of recursive function calls. After the correct value is found, any additional searches made will return incorrect values from that point on.
A couple ways to handle this:
1. Cancel the search
When the correct value is found, immediately return it all the way up the recursive stack, without searching any more of the current arrays or the nested arrays. In other words, cancel the rest of the search.
#Barmer's answer is an example of this. The key part of his code is the use of for loops rather than the each method to iterate through the arrays, since it's much easier to interrupt a for loop.
2. Store the value somewhere safe
When the correct value is found, store it somewhere safe, allow the rest of the search to continue, and after the initial function call has finished access the value. The simplest way is to store the correct value in a global variable, though that's not a good practice as it violates the encapsulation of the function.
#shyam's answer presents a cleaner solution: Passing a reference to a global variable as a function parameter, setting the parameter when the correct value is found, and then accessing the global variable after the initial function call has finished.
Choosing between the two
In laymen's terms, the intended logic of the function could be summed up as follows: When you find what you're looking for, stop, and let me know what it is immediately. The only reason to continue searching would be if multiple pieces of data needed to be found. I'm assuming that's not the case here.
Of the two approaches, #2 is a quick-fix workaround that should work fine but will further confuse anyone who's trying to understand the intended logic of the function. Why is the search continuing if it's only looking for a single piece of data that's already been found?
#1 is a refactoring of the function so that it behaves more consistently with the intended logic, which would make the function easier to understand. The function stops searching when it finds what it needs.
I know this is an old post, but I can see 2 issues with this:
1) the recursive call isn't returning the result up the call stack i.e.
search(val, name)
Should be
return search(val, name)
2) it looks like you are using array.forEach(). The documentation states:
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool. Use a plain loop instead. If you are testing the array elements for a predicate and need a Boolean return value, you can use every() or some() instead. If available, the new methods find() or findIndex() can be used for early termination upon true predicates as well.
What this means is, effectively, when you find the result you are looking for you want to send it back up the call stack. Using array.forEach will continue to recursively look through the hierarchy and return all values, not just the one you are interested in. Therefore the last returned value maybe one you don't expect, like undefined! Therefore use a different method of iterating i.e.
for (var i = 0; i < ch.length; i++) {
var val = ch[i];
return search(val, name, ret);
}
The accepted answer does spoon feed you part of this answer but doesn't explain why. Hence this answer
Since you are recursing the return might be nested too deep for you to get a meaningful result. Instead you can try passing an extra argument to collect the results.
function search(obj, name, ret) {
console.log(obj["name"], ",", name, obj["name"] == name);
if (obj["name"] == name) {
ret.push(obj);
return
}
if (obj.children || obj._children) {
var ch = obj.children || obj._children;
ch.forEach(function(val) {
search(val, name, ret);
});
}
}
var result = [];
search(myJson, "VM10-Proc4", result)
Here is a solution using object-scan
// const objectScan = require('object-scan');
const myJson = {"name":"UCS - San Jose","type":"Minor","children":[{"name":"VM1","type":"Clear","children":[{"name":"VM1-Proc1","type":"Clear","children":[{"name":"VM1-Proc1-child1","type":"Clear"}]},{"name":"VM1-Proc2","type":"Clear"},{"name":"VM1-Proc3","type":"Clear"},{"name":"VM1-Proc4","type":"Clear"},{"name":"VM1-Proc5","type":"Clear"},{"name":"VM1-Proc6","type":"Clear"},{"name":"VM1-Proc7","type":"Clear"},{"name":"VM1-Proc8","type":"Clear"},{"name":"VM1-Proc9","type":"Clear"},{"name":"VM1-Proc10","type":"Clear"}]},{"name":"VM2","type":"Clear","children":[{"name":"VM2-Proc1","type":"Clear"},{"name":"VM2-Proc2","type":"Clear"},{"name":"VM2-Proc3","type":"Clear"},{"name":"VM2-Proc4","type":"Clear"},{"name":"VM2-Proc5","type":"Clear"},{"name":"VM2-Proc6","type":"Clear"},{"name":"VM2-Proc7","type":"Clear"},{"name":"VM2-Proc8","type":"Clear"},{"name":"VM2-Proc9","type":"Clear"},{"name":"VM2-Proc10","type":"Clear"}]},{"name":"VM3","type":"Clear","children":[{"name":"VM3-Proc1","type":"Clear"},{"name":"VM3-Proc2","type":"Clear"},{"name":"VM3-Proc3","type":"Clear"},{"name":"VM3-Proc4","type":"Clear"},{"name":"VM3-Proc5","type":"Clear"},{"name":"VM3-Proc6","type":"Clear"},{"name":"VM3-Proc7","type":"Clear"},{"name":"VM3-Proc8","type":"Clear"},{"name":"VM3-Proc9","type":"Clear"},{"name":"VM3-Proc10","type":"Clear"}]},{"name":"VM4","type":"Minor","children":[{"name":"VM4-Proc1","type":"Clear"},{"name":"VM4-Proc2","type":"Clear"},{"name":"VM4-Proc3","type":"Minor"},{"name":"VM4-Proc4","type":"Clear"},{"name":"VM4-Proc5","type":"Clear"},{"name":"VM4-Proc6","type":"Minor"},{"name":"VM4-Proc7","type":"Clear"},{"name":"VM4-Proc8","type":"Clear"},{"name":"VM4-Proc9","type":"Clear"},{"name":"VM4-Proc10","type":"Clear"}]},{"name":"VM5","type":"Clear","children":[{"name":"VM5-Proc1","type":"Clear"},{"name":"VM5-Proc2","type":"Clear"},{"name":"VM5-Proc3","type":"Clear"},{"name":"VM5-Proc4","type":"Clear"},{"name":"VM5-Proc5","type":"Clear"},{"name":"VM5-Proc6","type":"Clear"},{"name":"VM5-Proc7","type":"Clear"},{"name":"VM5-Proc8","type":"Clear"},{"name":"VM5-Proc9","type":"Clear"},{"name":"VM5-Proc10","type":"Clear"}]},{"name":"VM6","type":"Minor","children":[{"name":"VM6-Proc1","type":"Clear"},{"name":"VM6-Proc2","type":"Clear"},{"name":"VM6-Proc3","type":"Minor"},{"name":"VM6-Proc4","type":"Clear"},{"name":"VM6-Proc5","type":"Clear"},{"name":"VM6-Proc6","type":"Clear"},{"name":"VM6-Proc7","type":"Minor"},{"name":"VM6-Proc8","type":"Clear"},{"name":"VM6-Proc9","type":"Clear"},{"name":"VM6-Proc10","type":"Clear"}]},{"name":"VM7","type":"Clear","children":[{"name":"VM7-Proc1","type":"Clear"},{"name":"VM7-Proc2","type":"Clear"},{"name":"VM7-Proc3","type":"Clear"},{"name":"VM7-Proc4","type":"Clear"},{"name":"VM7-Proc5","type":"Clear"},{"name":"VM7-Proc6","type":"Clear"},{"name":"VM7-Proc7","type":"Clear"},{"name":"VM7-Proc8","type":"Clear"},{"name":"VM7-Proc9","type":"Clear"},{"name":"VM7-Proc10","type":"Clear"}]},{"name":"VM8","type":"Clear","children":[{"name":"VM8-Proc1","type":"Clear"},{"name":"VM8-Proc2","type":"Clear"},{"name":"VM8-Proc3","type":"Clear"},{"name":"VM8-Proc4","type":"Clear"},{"name":"VM8-Proc5","type":"Clear"},{"name":"VM8-Proc6","type":"Clear"},{"name":"VM8-Proc7","type":"Clear"},{"name":"VM8-Proc8","type":"Clear"},{"name":"VM8-Proc9","type":"Clear"},{"name":"VM8-Proc10","type":"Clear"}]},{"name":"VM9","type":"Clear","children":[{"name":"VM9-Proc1","type":"Clear"},{"name":"VM9-Proc2","type":"Clear"},{"name":"VM9-Proc3","type":"Clear"},{"name":"VM9-Proc4","type":"Clear"},{"name":"VM9-Proc5","type":"Clear"},{"name":"VM9-Proc6","type":"Clear"},{"name":"VM9-Proc7","type":"Clear"},{"name":"VM9-Proc8","type":"Clear"},{"name":"VM9-Proc9","type":"Clear"},{"name":"VM9-Proc10","type":"Clear"}]},{"name":"VM10","type":"Clear","children":[{"name":"VM10-Proc1","type":"Clear"},{"name":"VM10-Proc2","type":"Clear"},{"name":"VM10-Proc3","type":"Clear"},{"name":"VM10-Proc4","type":"Clear"},{"name":"VM10-Proc5","type":"Clear"},{"name":"VM10-Proc6","type":"Clear"},{"name":"VM10-Proc7","type":"Clear"},{"name":"VM10-Proc8","type":"Clear"},{"name":"VM10-Proc9","type":"Clear"},{"name":"VM10-Proc10","type":"Clear"}]}]};
const search = (obj, name) => objectScan(['**.name'], {
rtn: 'parent',
abort: true,
filterFn: ({ value }) => value === name
})(obj);
console.log(search(myJson, 'VM10-Proc4'));
// => { name: 'VM10-Proc4', type: 'Clear' }
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.7.1"></script>
Disclaimer: I'm the author of object-scan
Recently i found myself attaching function arguments to a variable inside the function scope so that i was not referencing the argument every time it was used.
Is there any benefit to this practice?
For example:
function populateResultCount(count){
var count = count;
return $('.resultCounter').text(count);
};
Could easily be re-written like so:
function populateResultCount(count){
return $('.resultCounter').text(count);
};
And would still function correctly.
There's no functional difference between the two. Go with the simpler version.
If you're not using the argument that's passed in, there is no difference. In your first example, you can potentially confuse future maintainers because of var count = count, i.e., you're declaring a variable that has the same name as the argument, and that isn't a best practise.
So, if you can, use your second form. Its intent is clearer and there is no room for confusion.
I can see no benefit to this unless you are manipulating the data somehow. Your variable without the additional assingment can still not be accessed outside of the function.
function Test (count) {
this.increment = function() {
count++;
}
this.getCount = function() {
return count;
}
}
var test = new Test(10);
<button onclick="test.increment(); alert(test.getCount());">Increment</button>
You can do something like that even with the argument. So I think they are same.
All the other answers are correct: There's no reason to "re-assign" a passed argument inside the function.
The only thing I can think of, where you'd mess with reassigning arguments, is if you have optional arguments/default values
function xyz(optionalArgument) {
optionalArgument = optionalArgument || "no argument given";
...
}
But in that case, it'd be better to write it as
function xyz( /* optionalArgument */ ) {
var optionalArgument = arguments[0] || "no argument given";
...
}
Note that the || trick will give you the right-hand side's value, if the left-hand side is a falsy value. I.e. if you're ok with the optional argument being something that's falsy (like explicitly passing null, 0, etc), you'd have to do something like var arg = typeof arguments[x] === 'undefined' ? defaultValue : arguments[x];
I have now seen 2 methods for determining if an argument has been passed to a JavaScript function. I'm wondering if one method is better than the other or if one is just bad to use?
function Test(argument1, argument2) {
if (Test.arguments.length == 1) argument2 = 'blah';
alert(argument2);
}
Test('test');
Or
function Test(argument1, argument2) {
argument2 = argument2 || 'blah';
alert(argument2);
}
Test('test');
As far as I can tell, they both result in the same thing, but I've only used the first one before in production.
Another Option as mentioned by Tom:
function Test(argument1, argument2) {
if(argument2 === null) {
argument2 = 'blah';
}
alert(argument2);
}
As per Juan's comment, it would be better to change Tom's suggestion to:
function Test(argument1, argument2) {
if(argument2 === undefined) {
argument2 = 'blah';
}
alert(argument2);
}
There are several different ways to check if an argument was passed to a function. In addition to the two you mentioned in your (original) question - checking arguments.length or using the || operator to provide default values - one can also explicitly check the arguments for undefined via argument2 === undefined or typeof argument2 === 'undefined' if one is paranoid (see comments).
Using the || operator has become standard practice - all the cool kids do it - but be careful: The default value will be triggered if the argument evaluates to false, which means it might actually be undefined, null, false, 0, '' (or anything else for which Boolean(...) returns false).
So the question is when to use which check, as they all yield slightly different results.
Checking arguments.length exhibits the 'most correct' behaviour, but it might not be feasible if there's more than one optional argument.
The test for undefined is next 'best' - it only 'fails' if the function is explicitly called with an undefined value, which in all likelyhood should be treated the same way as omitting the argument.
The use of the || operator might trigger usage of the default value even if a valid argument is provided. On the other hand, its behaviour might actually be desired.
To summarize: Only use it if you know what you're doing!
In my opinion, using || is also the way to go if there's more than one optional argument and one doesn't want to pass an object literal as a workaround for named parameters.
Another nice way to provide default values using arguments.length is possible by falling through the labels of a switch statement:
function test(requiredArg, optionalArg1, optionalArg2, optionalArg3) {
switch(arguments.length) {
case 1: optionalArg1 = 'default1';
case 2: optionalArg2 = 'default2';
case 3: optionalArg3 = 'default3';
case 4: break;
default: throw new Error('illegal argument count')
}
// do stuff
}
This has the downside that the programmer's intention is not (visually) obvious and uses 'magic numbers'; it is therefore possibly error prone.
If you are using jQuery, one option that is nice (especially for complicated situations) is to use jQuery's extend method.
function foo(options) {
default_options = {
timeout : 1000,
callback : function(){},
some_number : 50,
some_text : "hello world"
};
options = $.extend({}, default_options, options);
}
If you call the function then like this:
foo({timeout : 500});
The options variable would then be:
{
timeout : 500,
callback : function(){},
some_number : 50,
some_text : "hello world"
};
This is one of the few cases where I find the test:
if(! argument2) {
}
works quite nicely and carries the correct implication syntactically.
(With the simultaneous restriction that I wouldn't allow a legitimate null value for argument2 which has some other meaning; but that would be really confusing.)
EDIT:
This is a really good example of a stylistic difference between loosely-typed and strongly-typed languages; and a stylistic option that javascript affords in spades.
My personal preference (with no criticism meant for other preferences) is minimalism. The less the code has to say, as long as I'm consistent and concise, the less someone else has to comprehend to correctly infer my meaning.
One implication of that preference is that I don't want to - don't find it useful to - pile up a bunch of type-dependency tests. Instead, I try to make the code mean what it looks like it means; and test only for what I really will need to test for.
One of the aggravations I find in some other peoples' code is needing to figure out whether or not they expect, in the larger context, to actually run into the cases they are testing for. Or if they are trying to test for everything possible, on the chance that they don't anticipate the context completely enough. Which means I end up needing to track them down exhaustively in both directions before I can confidently refactor or modify anything. I figure that there's a good chance they might have put those various tests in place because they foresaw circumstances where they would be needed (and which usually aren't apparent to me).
(I consider that a serious downside in the way these folks use dynamic languages. Too often people don't want to give up all the static tests, and end up faking it.)
I've seen this most glaringly in comparing comprehensive ActionScript 3 code with elegant javascript code. The AS3 can be 3 or 4 times the bulk of the js, and the reliability I suspect is at least no better, just because of the number (3-4X) of coding decisions that were made.
As you say, Shog9, YMMV. :D
In ES6 (ES2015) you can use Default parameters
function Test(arg1 = 'Hello', arg2 = 'World!'){
alert(arg1 + ' ' +arg2);
}
Test('Hello', 'World!'); // Hello World!
Test('Hello'); // Hello World!
Test(); // Hello World!
url = url === undefined ? location.href : url;
There are significant differences. Let's set up some test cases:
var unused; // value will be undefined
Test("test1", "some value");
Test("test2");
Test("test3", unused);
Test("test4", null);
Test("test5", 0);
Test("test6", "");
With the first method you describe, only the second test will use the default value. The second method will default all but the first (as JS will convert undefined, null, 0, and "" into the boolean false. And if you were to use Tom's method, only the fourth test will use the default!
Which method you choose really depends on your intended behavior. If values other than undefined are allowable for argument2, then you'll probably want some variation on the first; if a non-zero, non-null, non-empty value is desired, then the second method is ideal - indeed, it is often used to quickly eliminate such a wide range of values from consideration.
I'm sorry, I still yet cant comment, so to answer Tom's answer...
In javascript (undefined != null) == false
In fact that function wont work with "null", you should use "undefined"
There is a tricky way as well to find, whether a parameter is passed to a function or not. Have a look at the below example:
this.setCurrent = function(value) {
this.current = value || 0;
};
This necessary means that if the value of value is not present/passed - set it to 0.
Pretty cool huh!
Why not using the !! operator? This operator, placed before the variable, turn it to a boolean (if I've understood well), so !!undefined and !!null (and even !!NaN, which can be quite interesting) will return false.
Here is an exemple:
function foo(bar){
console.log(!!bar);
}
foo("hey") //=> will log true
foo() //=> will log false
Sometimes you want undefined as a possible argument but you still have situations where the argument may not be passed. In that case you can use arguments.length to check how many arguments were passed.
// Throw error if the field is not matching our expectations
function testField(label, fieldValue, expectedValue) {
console.log(arguments) // Gives: [Arguments] { '0': 'id', '1': 1, '2': undefined }
if(arguments.length === 2) {
if(!fieldValue) {
throw new Error(`Field "${label}" must have a value`)
}
}
else if(expectedValue === undefined) {
if(fieldValue !== undefined) {
throw Error(`Field "${label}" must NOT have a value`)
}
}
// We stringify so our check works for objects as well
else {
if(JSON.stringify(fieldValue) !== JSON.stringify(expectedValue)) {
throw Error(`Field "${label}" must equal ${expectedValue} but was ${fieldValue}`)
}
}
}
testField('id', 12) -> Passes, we don't want id to be blank
testField('id', undefined, undefined) -> Passes, we want id to be undefined
testField('id', 12, undefined) -> Errors, we wanted id to be undefined
It can be convenient to approach argument detection by evoking your function with an Object of optional properties:
function foo(options) {
var config = { // defaults
list: 'string value',
of: [a, b, c],
optional: {x: y},
objects: function(param){
// do stuff here
}
};
if(options !== undefined){
for (i in config) {
if (config.hasOwnProperty(i)){
if (options[i] !== undefined) { config[i] = options[i]; }
}
}
}
}
Some times you may also want to check for type, specially if you are using the function as getter and setter. The following code is ES6 (will not run in EcmaScript 5 or older):
class PrivateTest {
constructor(aNumber) {
let _aNumber = aNumber;
//Privileged setter/getter with access to private _number:
this.aNumber = function(value) {
if (value !== undefined && (typeof value === typeof _aNumber)) {
_aNumber = value;
}
else {
return _aNumber;
}
}
}
}
function example(arg) {
var argumentID = '0'; //1,2,3,4...whatever
if (argumentID in arguments === false) {
console.log(`the argument with id ${argumentID} was not passed to the function`);
}
}
Because arrays inherit from Object.prototype. Consider ⇑ to make the world better.
fnCalledFunction(Param1,Param2, window.YourOptionalParameter)
If above function is called from many places and you are sure first 2 parameters are passed from every where but not sure about 3rd parameter then you can use window.
window.param3 will handle if it is not defined from the caller method.