JavaScript chained concatenation function? - javascript

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.

Related

Accessing parameters when the function expects none Javascript

I am writing a Javascript function to count the number of instances of an element in an unsorted array. It has a method signature like this
Array.prototype.numberOfOccurrences = function() {
}
Here is an example of expected behavior
var arr = [4, 0, 4];
Test.assertEquals(arr.numberOfOccurrences(4), 2);
My problem is that I don't know how to access the elements in the array. The function doesn't take any parameters so how do I reference the array being passed in?
Note: The instructions aren't very descriptive for this kata on code wars and adding a parameter to the function returns some error unexpected token.
Inside the function you are creating into the Array.prototype you can access all the prototype functions through the "this" keyword.
Meaning you can access the array items using numeric properties like this[0] or this[1] to a access the first and second item respectively.
You can also call functions which allows you to iterate over each item on the array, such as: forEach, filter, etc.
Please refer this page to see everything you can do with the array prototype:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/prototype
Lastly don't forget that the JavaScript implementation varies on each browser, so a function that works on Chrome, might not work on InternetExplorer, always confirm on caniuse.com If the function you are used has the same implementation on your targets browsers.
Cheers.
Whether you should extend javascript base objects aside, this is your friend:
Array.prototype.numberOfOccurrences = function(valueToFind) {
return this.filter(function(item) {
return item === valueToFind;
}).length;
}
var a = [1,2,3,3,3,3];
console.log(a.numberOfOccurrences(3)); //4
As noted above, if you're not able to change the function signature for whatever reason you can specify it as follows:
Array.prototype.numberOfOccurrences = function() {
var valueToFind = arguments[0];
...
}
I would recommend adding the parameter to the function for clarities sake. Seems counter intuitive for a function like numberOfOccurences to not take in a parameter - numberOfOccurences of what?
Fiddle: http://jsfiddle.net/KyleMuir/g82b3f98/
You might try using the locally available variable 'arguments' inside of the function. So for example, your code might look like thsi:
Array.prototype.numberOfOccurrences = function() {
var args = arguments || {};
var testArray, testCheck;
if (args[0] && Array.isArray(args[0]) {
// do something with the array that was the first argument, like:
testArray = args[0];
testCheck = testArray.indexOf(args[1]);
return testCheck;
} else {
// do what you want to do if the function doesn't receive any arguments or the first argument
// received isn't an array.
}
}
'arguments' is always available to you inside a declared function.

How to make concatenation within javascript function

I want to call the javascript functions like bbb(a1), bbb(a2), bbb(a3)......
through the function
function sss()
{
for (i=1;i<=3;i++){
bbb(a.i)
}
}
sss();
There must be many errors in the above code. What should be the correct syntax? Specially in bbb(a.i) i.e., to concatenate 'a' with variable 'i'.
I'm not 100% sure of your intentions, but re-reading your question a couple of times, I think you're looking to build a string out of the iterator variable i to pass to bbb(). If so, you'd want this:
function sss() {
for (var i = 1; i <= 3; i++) {
bbb("a" + i);
}
}
This will effectively do:
bbb("a1");
bbb("a2");
bbb("a3");
Also note the introduction of var before i = 1 -- you definitely don't want to accidentally create global iterator variables.
If I'm understanding you right, there aren't any syntax errors, but there are a couple of others:
function sss()
{
var i; // <=== Declare `i`
for (i=1;i<=3;i++){
bbb(a[i]); // <=== Use [i] to access `a[1]`, `a[2]`, etc.
}
}
sss();
Note that if a is a normal, non-sparse array, you probably want:
for (i=0;i<=a.length;i++){
In JavaScript, you can access properties using dot syntax with a literal (obj.foo), or bracketed syntax with what is technically coerced to a string (obj["foo"]). The second syntax is what we typically use for arrays, using a[0], a[1], etc. (which are technically, theoretically coerced to a["0"], a["1"], etc., because regular JavaScript arrays aren't really arrays at all.)

Edit Eval()'d JSON - Javascript

Take the following object. I'm trying to pass a string path to "gold", eval() it and then increment it like so:
var inv = {
"gold" : 10
}
eval('inv.gold')++;
It's not working however. I've tried using JSON.parse as well but it didn't work (whether it was me writing it wrong or something else I don't know).
What is the correct way to do this?
eval() will evaluate or execute an argument, so you'll need the ++ inside of that:
eval('inv.gold++');
JSFiddle
You probably don't want to eval here, but simply use an iterator function. For instance:
function setProperty(object, chain, value) {
if(typeof chain === "string") {
chain = chain.split(".");
}
if(chain.length === 1) {
object[chain[0]] = value;
} else {
setProperty(object, chain.slice(1), value);
}
}
But the real question is of course why you need to set the value based on a full accessor chain, because that's usually an indication something's not quite right (outside of writing a parser or the like)

Is it possible to call a method from an object using a string?

Is it possible to call a method from an object using a string?
var elem = $('#test'); //<div id="test"></div>
var str = "attr('id')";
//This is what I'm trying to achieve
elem.attr('id'); //test
//What I've tried so far
elem.str; //undefined
elem.str(); //Object [object Object] has no method 'str'
var fn = eval(str); //attr is not defined
eval(elem.toString()+'.'+str); //Unexpected identifier
//Only solution I've found so far,
//but is not an option for me
//because this code is in a function
//so the element and method call
//get passed in and I wouldn't know
//what they are
eval($('#test').attr('id')); //test
UPDATE
This is my final, working answer:After running this code in the console
theMethod = 'attr("id","foo")'.match(/^([^(]+)\(([^)]*)\)/);
jQuery('#post-form')[theMethod[1]].apply(jQuery('#post-form'),JSON.parse('['+theMethod[2]+']'));
The post-form element now has a new ID, no problems at all. This works for methods that take multiple arguments, a single argument or no arguments at all. Recap:
theMethod = theInString.match(/^\.?([^(]+)\(([^)]*)\)/);
//added \.? to trim leading dot
//made match in between brackets non-greedy
//dropped the $ flag at the end, to avoid issues with trailing white-space after )
elem[theMethod[1]].apply(elem,JSON.parse('['+theMethod+']'));
That's the safest, most reliable approach I can think of, really
What ever you do DON'T USE EVAL:
var theMethod = 'attr(\'id\')';
//break it down:
theMethod = theMethod.match(/^([^(]+)\(.*?([^)'"]+).*\)$/);
//returns ["attr('id')", "attr", "id"]
elem[theMethod[1]](theMethod[2]);//calls the method
It's the same basic principle as you'd use with any objects (remember that functions are objects all on their own in JS - and jQuery objects are, well, objects, too). This means that methods can be accessed in the exact same way as properties can:
$('#foo').attr('id') === $('#foo')['attr']('id');
So just break the string apart, and use the method name like you would an object property and you're all set to go.
Just remember: When all you have is the eval hammer, everything looks like your thumb.
Brendan Eich
If there is a chance of multiple arguments being passed to whatever method, you can sort of work your way around that, too (I think - well: logic dictates, but it's rather late and logic is getting beat up by Gin pretty bad now):
theMethod = theMethod.match(/^([^(]+)\(([^)]+)\)$/);
//["attr('id','foo')", "attr", "'id','foo'"] --> regex must now match quotes, too
elem.theMethod[1].apply(elem,JSON.parse('['+theMethod[2]+']'));
This applies the method of whatever element/object you're dealing with to itself, thus not changing the caller context (this will still point to the object within the method) and it passes an array of arguments that will be passed to the called method.
You should use one of these methods:
apply
var result = function.apply(thisArg[, argsArray]);
call
var result = fun.call(thisArg[, arg1[, arg2[, ...]]]);
Here is the sample:
var Sample = function() {
var that = this;
this.sampleMethod = function() {
return alert("Hello!");
};
this.sampleMethod2 = function(){
that["sampleMethod"].apply(that);
};
};
var objImpl = new Sample();
objImpl.sampleMethod2(); //you will get a message from 'sampleMethod()'
Eval does what you want to do. Eval is evil, however, because you should not do what you want to do.
Why is using the JavaScript eval function a bad idea?

Copying a subset of the arguments object, without using an explicit loop

I have a JavaScript function that takes two required parameters and then arbitrarily many optional parameters.
function myFunction(required1, required2) {
var more = [];
for (var i = 2; i < arguments.length; ++i)
more.push(arguments[i]);
// ...
}
Now, I like enforcing a consistent style through all my code. Since my site uses jQuery, and jQuery favors using $.each and $.map over explicit loops, I want to get rid of the explicit loop in myFunction. However, I cannot use either $.each or $.map because I don't want to copy the whole argument list, lest I do the following:
var more = $.map(arguments, function(argument, index) {
return (index < 2) ? null : [argument];
});
Which is, of course, a very bad idea, because testing whether index < 2 in every iteration is unnecessary.
I would really like to be able to extract a subset of the arguments object into a new array, using a standard function. However, because arguments is not an array, I cannot slice it.
Is there any other way I could extract into an array all arguments but the two first ones, without using an explicit loop, and without losing efficiency?
Using the slice method:
var optional_arguments = Array.prototype.slice.call(arguments, 2);
We have to call it from Array.prototype because, even though arguments is array-like, it doesn't actually have a slice method.
The arguments of a function can be treated like an array with jQuery, for instance the following works perfectly fine:
function something(ar1,ar2){
var args = $(arguments).slice(2);
$(args).each(function(i){
$('#stuff').append(args[i]+'<br />');
});
}
$(function(){
something('one','two','three','four');
});
And to tailor it to your function above it would look like this:
function myFunction(required1, required2) {
var more = [];
var args = $(arguments).slice(2);
$(args).each(function(i){
more.push(args[i]);
});
}

Categories

Resources