JavaScript: Functional mapping? - javascript

Is there a more succinct way to write this?
var me = {};
for (var i in you) {
me[i] = you[i];
}
(where you is an arbitrarily-lengthed JavaScript array)
In other words, given the input of:
var you = [
"what",
"r",
"u"
];
The output, me, becomes:
me = {
0: "what",
1: "r",
2: "u"
};
Like, a one-liner that uses some functional method?

Why do you want to do this? Arrays in JavaScript are Objects, except with some additional properties like slice, splice, push and length.
Internally, Arrays and Objects are stored exactly in the same way: e.g. array[0] is the same as array["0"], or object["0"] (unlike in other languages where adjacent array indices are in fact in adjacent memory - array "indices" are simply converted into strings in JavaScript).
So, if you just want to copy the data, then this will suffice:
me = you.slice(); // me is a copy of you, but is still an array
Or, if you really want some sort of mapping functionality, then underscore.js provides a whole collection of functional programming tools for your perusal.

There is no built-in function that does what you ask, however some of the widely used javascript libraries like jQuery provide such a function. In jQuery's case: jQuery.extend()
Usage:
var me = {};
jQuery.extend(me,someObject);
//or, equivalently -
var me2 = jQuery.extend({},someObject);

jQuery has an extend() function (documentation here). Your code would look like this:
var me = {};
var you = ["what", "r", "u"];
$.extend(me, you);
This would allow you to do things like:
alert("Second element: " + me[1]);
It's a little odd, but I think it's what you're looking for.

I saw what you were trying to achieve with your string formatter. Instead of answering your original question of coming up with a concise implementation of one portion of it, I'll suggest a concise (and more flexible) implementation for the whole thing:
String.prototype.format = function () {
var args = arguments;
return this.replace(/\{(?:(\d+)|(\w+))\}/g, function (s, idx, prop) {
return prop && args[0]
? args[0][prop]
: args[idx];
});
};
When you have a number n inside a token "{n}", it uses the n-th argument for replacement. Otherwise, for non-numerical keys, it picks the corresponding property of the first argument.
For example:
"I have {1} {name}s in my basket.".replace({ type: "fruit", name: "eggplant" }, 4);
Returns:
"I have 4 eggplants in my basket."

The underscore.js library also has a basic extend function.
var me = _({}).extend(you)
or
var me = {}
_(me).extend(you)
or
var me = {}
_.extend(me, you)

Related

Use arrays as keys for a dict

Note:
Asking about alternative methods for performing a task is not a matter of opinion.
Asking about pros and cons of such methods is not a matter of opinion.
This is what I am asking.
Asking which method is preferable, based on such pros and cons, is a matter of opinion.
This is not what I am asking.
I mean to use a dict (or a similar object) in Google apps script, with arrays as keys.
These (I didn't know) are converted to strings when used as dict keys.
The way I wrote my code, for what I needed, was working.
After I added some functionality, the limitations surfaced.
For instance, I want keys to be something like [<color>,<number>].
Then I mean to do
for (key in mydict) {
var c = key[0];
var n = key[1];
... work separately with c as a string and n as a number
}
I guess I could split key at the comma, and convert n to int, but if there is a less cumbersome alternative, I would go for that.
Plus, in a more general case the conversion of key back to its original type may be more involved.
One solution I found is with WeakMap.
I am currently trying it.
Regardless the fact it may work, is there any alternative?
What are possible pros and cons of those other options?
It would be very good to know before rewriting all code.
Use nested objects.
mydict = {
"color1": {
1: <somevalue>,
5: <othervalue>
},
"color2": {
3: <value3>,
10: <value4>
}
};
Then use nested loops:
Object.entries(mydict).forEach(([c, item]) =>
Object.entries(item).forEach([n, value]) => {
// do something with c, n, value
})
);
One simple option is
for (keystr in mydict) {
var key = keystr.split(',');
var c = key[0];
var n = Number(key[1]);
... work separately with c as a string and n as a number
}
which works as long as none of the elements in the keys contain ','.

Using a variable as a reference level in an object

I'm quite new to coding html/js. I checked answers on here but either can't understand them well enough or they aren't quite what I'm looking for.
The problem is straight forward enough; if I have the following object;
var gadget = {"1":{"id":A, "name":"Rawr"},"2":{"id":B, "name":"GoGoGadget"}"1":{"id":C, "name":"Extendable Arms!"}};
Now, if I wanted to use a forloop (for whatever reason) to extract the names of these objects I would like to try;
var i = 0;
var names = [];
for (i = 0; i < gadget.length; i++) {
names.push(gadget.i.name);
}
I'm not surprised that this doesn't work as the "i" would probably got interpreted as the string "i" here rather than it's numeral. Unfortunately I've tried a few variants that I've found online (like using names.push(gadget[i].name) which also shouldn't work since that suggests gadget is a vector and not an object) but haven't been able to figure out how to get it to work.
Is there somewhere I can find this syntax? Or is this one of those things that seems like it should be easy but js just doesn't really have a nice solution for?
Thanks!
edit:
I didn't mean to suggest I always wanted every entry in the vector, so to clarify my question further, what if I wanted to use a forloop to only find the names of gadget entries given in another object; ie given;
var searchvec = [{"id":1,"count":17},{"id":3,"count":12}];
var i = 0;
var names = [];
for (i = 0; i < searchvec.length; i++) {
index = searchvec.i.id;
names.push(gadget.index.name);
}
I think this is what you are looking for:
var gadget = {"1": {"id": "A", "name": "Rawr"}, "2": {"id": "B", "name": "GoGoGadget"}, "3": {"id": "C", "name": "Extendable Arms!"}};
let names = [];
for (let i in gadget) {
// i is String
names.push(gadget[i].name);
}
for (let i in names) {
// i is int value
console.log(names[i]);
}
Using the for (let x IN o) js takes care for you no matter wether it is an object or an array.
With array, the for loop will cycle through the the array indexes, with x being an int value, and with objects it will cycle through the properties of the object, with x being the property name.
See reference:
MDN
Javascript is a really quirky language and can cause you serious hitches if you are used to more rigorous language. In js an array is actually an object with numbers as keys + some other goodies (such as a push() method) (to see it you may try in a console: typeof []))
P.S. this means you can actually access object properties with obj[key] being key a variable, even null-valued.
You can do something like below:
var names = [];
for (let [key,value] of Object.entries(gadgets)){
names.push(value)
}

How to pass an array as argument to a function in javascript?

I'm trying to make helper functions to make use of the Google Analytics API, and I have a simple problem building strings. The scenario is, I have to apply a filter, and there may be n number of filters (a nominal amount, not more than 128 anyhow). I wanted to write a function which can take in the n strings and combine them with comma-separation in between.
I don't know if the number of arguments can be variable in javascript, and if it can take arrays as arguments in javascript (I am a newbie to JS), but I see no difference as variables are simply var and there is no datatype anywhere in JS (I come from a C++/Java background and find it confusing as it is). So I tried passing an array as an argument to a function so that the no. of things I can work with can be dynamic, decided by the elements in the array.
When I started searching for solutions, I came across this page. After that I recently came across this thread which also refers the same link and the format they have provided does me no good.
For the sake of clarity, let me provide the function definition I've written.
/**
* Utility method to build a comma-ed string from an array of strings
* for the multiple-condition requests to the GA API
*/
function buildString(strArray)
{
var returnString='';
for(var x in strArray)
returnString+=x+',';
return returnString = returnString.substring(0, returnString.length - 1);
}
And this is how I call it:
buildString.apply(this,[desc(visits),source])
where desc(visits) and source are both strings, so I assumed I'm sending an array of strings. Strangely, both this and null in the apply() call to the buildString function give me "0,1" as the return value.
Please tell me where I'm going wrong. Am I passing the array in a wrong manner? Or is my function definition wrong? Or is there some other simpler way to achieve what I'm trying?
Passing arrays to functions is no different from passing any other type:
var string = buildString([desc(visits), source]);
However, your function is not necessary, since Javascript has a built-in function for concatenating array elements with a delimiter:
var string = someArray.join(',');
You're over complicating things — JavaScript arrays have a built-in join method:
[ desc( visits ), source ].join( ',' );
EDIT: simpler still: the toString method:
[ desc( visits ), source ].toString();
The easiest would be to use the built-in join method:
[desc(visits), source].join(',');
Anyway, your problem was in the for..in loop
Instead of this:
for(var x in strArray){
returnString+=x+',';
}
You should have:
for(var i in strArray){
var x = strArray[i]; //Note this
returnString+=x+',';
}
Because for...in gives back the index/key, not the actual element as foreach does in other languages
Also your call should be:
buildString.call(this,[desc(visits),source]) or just buildString([desc(visits),source])
Cheers
Js argument can be any type and no limit to the number of argument,
But it is recommanded use 3-4 arguments at most, if there are more args, you can pass it as an object or array.
You don't need to worry about the type of args, js will do the job.
For example:
var func1 = function(a) {
console.log(a);
}
func1('good');
func1(1);
func1(['good', 'a', 1]);
func1({name: 'fn1', age: 12});
Anything you like!,
You can even define a function with three arguments, but only pass one is ok!
var func2 = function(a, b, c) {
console.log(a);
}
func2(1);
func2(1, 'good');
func2(1, 'good', 'night', 4);
And default array obj has many build-in func; for example:
var arr = ['good', 'night', 'foo', 'bar']; //define any thing in a array
str = arr.join(','); //you may get 'good,night,foo,bar'
var arr1 = str.split(','); // you may get ['good', 'night', 'foo', 'bar'];

Javascript: using tuples as dictionary keys

I have a situation where I want to create a mapping from a tuple to an integer. In python, I would simply use a tuple (a,b) as the key to a dictionary,
Does Javascript have tuples? I found that (a,b) in javascript as an expression just returns b (the last item). Apparently this is inherited from C.
So, as a workaround, I thought I can use arrays instead,
my_map[[a,b]] = c
I tried it at the Firebug console and it seemed to work. Is that a good way to do it?
Another alternative I thought of is to create a string out of the tuples
my_map[""+a+":"+b] = c
So the question is: is there any problem with any of these methods? Is there a better way?
EDIT:
Small clarification: in my case, a,b,c are all integers
EcmaScript doesn't distinguish between indexing a property by name or by [], eg.
a.name
is literally equivalent to
a["name"]
The only difference is that numbers, etc are not valid syntax in a named property access
a.1
a.true
and so on are all invalid syntax.
Alas the reason all of these indexing mechanisms are the same is because in EcmaScript all property names are strings. eg.
a[1]
is effectively interpreted as
a[String(1)]
Which means in your example you do:
my_map[[a,b]] = c
Which becomes
my_map[String([a,b])] = c
Which is essentially the same as what your second example is doing (depending on implementation it may be faster however).
If you want true value-associative lookups you will need to implement it yourself on top of the js language, and you'll lose the nice [] style access :-(
You could use my jshashtable and then use any object as a key, though assuming your tuples are arrays of integers I think your best bet is one you've mentioned yourself: use the join() method of Array to create property names of a regular object. You could wrap this very simply:
function TupleDictionary() {
this.dict = {};
}
TupleDictionary.prototype = {
tupleToString: function(tuple) {
return tuple.join(",");
},
put: function(tuple, val) {
this.dict[ this.tupleToString(tuple) ] = val;
},
get: function(tuple) {
return this.dict[ this.tupleToString(tuple) ];
}
};
var dict = new TupleDictionary();
dict.put( [1,2], "banana" );
alert( dict.get( [1,2] ) );
All object keys in Javascript are strings. Using my_map[[a,b]] = c will produce a key in my_map which is the result of [a,b].toString(): a.toString() + ',' + b.toString(). This may actually be desirable (and is similar to your use of a + ':' + b), but you may run into conflicts if your keys contain the separator (either the comma if you use the array as the key, or the colon if you write the string as you have in your example).
Edit: An alternate approach would be to keep a separate array for key references. Eg:
var keys = [
[a,b],
[c,d]
];
var my_map = {
'keys[0]': /* Whatever [a,b] ought to be the key for */,
'keys[1]': /* Whatever [c,d] ought to be the key for */
};
the most simple and "natural" way to achieve something similar is by using multidimensional arrays, like this:
var my_map = [["blah","blah","bla"],
["foo", "bla", 8],
[324, 2345, 235],
[true, false, "whatever..."]];

A better way to splice an array into an array in javascript

Is there a better way than this to splice an array into another array in javascript
var string = 'theArray.splice('+start+', '+number+',"'+newItemsArray.join('","')+'");';
eval(string);
You can use apply to avoid eval:
var args = [start, number].concat(newItemsArray);
Array.prototype.splice.apply(theArray, args);
The apply function is used to call another function, with a given context and arguments, provided as an array, for example:
If we call:
var nums = [1,2,3,4];
Math.min.apply(Math, nums);
The apply function will execute:
Math.min(1,2,3,4);
UPDATE: ES6 version
If you're coding in ES6, you can use the "spread operator" (...).
array.splice(index, 0, ...arrayToInsert);
To learn more about the spread operator see the MDN documentation.
The 'old' ES5 way
If you wrap the top answer into a function you get this:
function insertArrayAt(array, index, arrayToInsert) {
Array.prototype.splice.apply(array, [index, 0].concat(arrayToInsert));
}
You would use it like this:
var arr = ["A", "B", "C"];
insertArrayAt(arr, 1, ["x", "y", "z"]);
alert(JSON.stringify(arr)); // output: A, x, y, z, B, C
You can check it out in this jsFiddle: http://jsfiddle.net/luisperezphd/Wc8aS/
This question is really old, but with ES6, there's a simpler way to do this using the spread operator:
sourceArray.splice(index, 0, ...insertedArray)
If you're using uncompiled javascript in the browser, be sure to check if it's supported in your target browser at https://kangax.github.io/compat-table/es6/#test-spread_(...)_operator.
Also, this may be slightly off topic, but if you don't want or need to modify the original array, but could use a new array instead, consider this approach:
mergedArray = sourceArray.slice(0, index).concat(insertedArray, sourceArray.slice(index))
You can also add such a function to the Array prototype, if you want something that is almost identical to the splice method. E.g.
Array.prototype.spliceArray = function(index, n, array) {
return Array.prototype.splice.apply(this, [index, n].concat(array));
}
Then usage would simply be:
var array = ["A","B","C","","E","F"];
array.splice(3,1,"D");
// array is ["A","B","C","D","E","F"]
array.spliceArray(3,3,["1","2","3"]);
// array is ["A","B","C","1","2","3"]
See it in action here: http://jsfiddle.net/TheMadDeveloper/knv2f8bb/1/
Some notes:
The splice function modifies the array directly, but returns the an array of elements that were removed... not the spliced array.
While it's normally not recommended to extend core javascript classes, this is relatively benign with most standard frameworks.
Extending Array won't work in cases where specialized array classes are used, such as an ImageData data Uint8ClampedArray.
The answers above that involve splice.apply and insert the array in a one liner will blow up the stack in a stack overflow for large array.
See example here:
http://jsfiddle.net/gkohen/u49ku99q/
You might have to slice and and push each item of the inserted and remaining part of the original array for it to work.
See fiddle: http://jsfiddle.net/gkohen/g9abppgy/26/
Array.prototype.spliceArray = function(index, insertedArray) {
var postArray = this.splice(index);
inPlacePush(this, insertedArray);
inPlacePush(this, postArray);
function inPlacePush(targetArray, pushedArray) {
// Not using forEach for browser compatability
var pushedArrayLength = pushedArray.length;
for (var index = 0; index < pushedArrayLength; index++) {
targetArray.push(pushedArray[index]);
}
}
}
There are a lot of clever answers here, but the reason you use splice is so that it puts the elements into the current array without creating another. If you have to create an array to concat() against so you can use apply() then you're creating 2 additional trash arrays! Sorta defeats the whole purpose of writing esoteric Javascript. Besides if you don't care about that memory usage stuff (and you should) just dest = src1.concat(src2); it is infinitely more readable. So here's is my smallest number of lines while staying efficient answer.
for( let item of src ) dest.push( item );
Or if you'd like to polyfill it and have a little better browser support back:
src.forEach( function( x ) { dest.push(x); });
I'm sure the first is more performant (it's a word ;), but not supported in all browsers out there in the wild.
If you don't want to concatenate inserting items to first two parameters of Array.splice(),
an elegant way is to use Function.bind() and Function.apply() together.
theArray.splice.bind(null, startIndex, deleteCount).apply(newItemsArray);
I wanted to have a function which would take only part of the source array so I have mine slightly different
based off CMS's answer
function spliceArray(array, index, howmany, source, start, end) {
var arguments;
if( source !== undefined ){
arguments = source.slice(start, end);
arguments.splice(0,0, index, howmany);
} else{
arguments = [index, howmany];
}
return Array.prototype.splice.apply(array, arguments)
}
Array.prototype.spliceArray = function(index, howmany, source, start, end) {
return spliceArray(this, index, howmany, source, start, end);
}
You can see it at: https://jsfiddle.net/matthewvukomanovic/nx858uz5/

Categories

Resources