Let's say I have a function and an array. I want to modify the array by applying the function to each entry in the array. The function does NOT modify the value directly; it returns a new value.
In pseudo-code,
for (entry in array) {
entry = function(entry);
}
There are a couple ways to do this that occurred to me:
for (var i = 0; i < arr.length; i++) {
arr[i] = fn(i);
}
Or, since I am using node.js and have underscore built in:
arr = _.map(arr, fn);
But this both seem a little clunky. The standard "for" block feels overly verbose, and the _.map function re-assigns the entire array so feels inefficient.
How would you do this?
Yes, I am aware I'm overthinking this :)
The Array#map() method.
var arr = arr.map(fn);
_.map() is probably implemented in the same way.
Related
So basically here is what I have, which works as intended -
let newList: any[] = [];
for (let stuff of this.Stuff) {
newList = newList.concat(stuff.food);
}
So basically Stuff is an array of objects where each object has a property that is another object called food. I want to go through this array of Stuff and create a new array with each instance of food within it.
I don't think the way I have done it is bad, I'm just wondering for my own curiosity how someone would have done this without the for loop.
Cheers.
You're looking for the map method:
const newList = this.Stuff.map(stuff => stuff.food);
You shouldn't need to use continuous reassignment to a variable, and it's certainly not functional :-) Also, using concat repeatedly is pretty inefficient, you'd better have used push within the for…of loop. But map is still simpler and better.
Try this approach as well using reduce method
var arr = [[1,2,3],[4,5,6],[7,8,9]];
var output = arr.reduce( function ( prev, curr ){ return prev.concat( curr ) } );
console.log(output);
You can express this nicely with an added functional primitive
const prop = x => y => y[x];
const newList = this.Stuff.map(prop('food'));
I am working on a super simple meteor app and found some code that does almost exactly what I want, but has a dependency on underscorejs. I have never used underscorejs and prefer not to have that dependency (I have certainly heard that underscore is great, but I just don't want to deal with any unnecessary packages at this time). This is the only line that uses underscorejs:
this.channels[name].args = _.toArray(arguments);
Would rewriting the toArray function be trivial or is there some heavy lifting going on underneath the hood?
The code came from this blog entry: http://www.manuel-schoebel.com/blog/meteorjs-package-only-app-structure-with-mediator-pattern and is located in the Mediator object example.
Thanks!
arguments is an iterable object, although it's not explicity an Array. If you don't care about JS engine implications, you can simply convert it to a real array. This allows you to treat arguments like an array and perform array methods. You can just do:
this.channels[name].args = Array.prototype.slice.call(arguments);
Another way is:
this.channels[name].args = Array.apply(null, arguments);
If you want to replace _.toArray() and not depend on the underscore library, you can do something like:
_.toArray = function () { return Array.prototype.slice.call(arguments)[0]; };
ES6 will have the spread operator which does what you want elegantly:
function someFunction(...args) {
this.channels[name].args = args;
}
If that is the native arguments object, you can iterate and create an array
var arr = [];
for (var i=0; i<arguments.length; i++) {
arr[i] = arguments[i]
}
or you can use the native Array.prototype.slice.call, shortened with [].slice.call
var arr = [].slice.call(arguments);
Note that MDN specifically says
You should not slice on arguments because it prevents optimizations in
JavaScript engines (V8 for example). Instead, try constructing a new
array by iterating through the arguments object.
If you rate underscore and trust what's it's doing, you could just use its toArray method found here: http://underscorejs.org/docs/underscore.html#section-46
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (isArrayLike(obj)) return _.map(obj, _.identity);
return _.values(obj);
};
Unfortunately, underscore uses underscore! So you'd have to implement isArray, and isArrayLike also :P
Maybe the source can give you some ideas for how you'd like your implementation to work?
Also, take a look at the lodash source. That too might provide some inspiration: https://github.com/lodash/lodash/blob/master/lodash.js#L8983
According to the 5.2.1 section of this article: Optimization killers
Doing this turns optimizations off in V8:
function hashTableIteration() {
var hashTable = {"-": 3};
for(var key in hashTable);
}
and the author says:
An object will go into hash table mode for example when you add too many properties dynamically (outside constructor), delete properties, use properties that cannot be valid identifiers and so on. In other words, when you use an object as if it was a hash
table, it will be turned into a hash table. Passing such an object to for-in is a no no. You can tell if an object is in hash table mode by calling console.log(%HasFastProperties(obj)) when the flag --allow-natives-syntax is enabled in Node.JS.
My question is then what is the correct way of iterating through the keys of a hashtable-like object in javascript, so that optimization do not get turned off?
Looks like the answer lies at the bottom of the very same article.
Workaround: Always use Object.keys and iterate over the array with
for loop. If you truly need all properties from entire prototype
chain, make an isolated helper function:
function inheritedKeys(obj) {
var ret = [];
for(var key in obj) {
ret.push(key);
}
return ret;
}
If you pass a object to for-in that is not a simple enumerable it
will punish the entire containing function.
From what I understood, the isolated function would help in allowing the rest of the process to be optimized. Only the inheritedkeys function wouldn't be optimized in the example below.
function someFunction(someObj) {
var keys = inheritedKeys(someObj),
i = 0,
len = keys.length;
for (; i < len; i++) {
//some computation
}
}
I believe Object.keys is regularly a better alternative in performance. However, I cannot say whether the optimizer converts it to a hashtable if used.
Update: Adding code for others stumbling upon this question.
var keys = Object.keys(myObj);
for (var i = 0; i < keys.length; i++) {
var value = myObj[keys[i]];
}
Arrays have a "length" property by default.
Can I add custom properties to them?
Without having to make them objects.
var arr = [1,2,3,4,5];
arr.prop = 'value';
Arrays are already objects in JavaScript -- they just have some extra features and special literal syntax.
As other answers state, it's perfectly possible, because arrays in JavaScript are just objects. However, there is still the a question of whether it's a good idea or not.
That's a "coding style" question, so it's hard to say objectively, but Douglas Crockford doesn't have a problem with it (at least in some cases). In JavaScript: The Good Parts, he actually uses the example of adding a "total" method to an array.
Because an array is really an object, we can add methods directly to an individual array:
// Give the data array a total function
data.total = function () {
return this.reduce(add, 0);
};
total = data.total(); // total is 108
Since the string 'total' is not an integer, adding a total property to an array does not change its length.
(p62, Crockford's "JavaScript The Good Parts", found on Google Books)
However, it is worth mentioning that these extra properties are not included in JSON serialisation, and would be thrown away if you do anything like arr = arr.slice(1);.
If you meant to hide it in an Array, etc. (at least that's what I was looking for):
Object.defineProperty( targ, "myVal", { enumerable: false, writable: true } );
Then the array doesn't add it to length, so it's a property, not a value I guess you could say.
Then if you want to see the keys in an object for instance, Object.keys won't see it, but Reflect.ownKeys() will.
The Reflect version of defineProperty will return success or fail, while Object's returns the object passed.
Remember Reflect.preventExtensions() will (like it says) stop you or others from extending them.
Arrays are objects and therefore you can add your own properties to them:
var arr = [1, 2, 3];
arr.foo = 'bar';
Extending from other answers, here are the following ways of iterating over the array, excluding custom properties.
Using a standard for loop
for (let i = 0; i < arr_length; i++)
console.log(arr[i]);
or if not using ES6:
for (var i = 0; i < arr.length; i++)
console.log(arr[i]);
Using the for ... of loop (ES6+)
for (let el of arr)
console.log(el)
Using Array.prototype.forEach (ES6+)
arr.forEach(el => console.log(el));
or
arr.forEach(function(el) {
console.log(el);
});
Iterating over the array, including all properties (e.g., custom properties, length):
for (let prop in arr)
console.log(prop);
Or if not using ES6:
for (var prop in arr)
console.log(prop);
Yes. You can add them to the object by just declaring them and you can also extend Array using Array.prototype
var j = new Array();
j.yourprop = "foo";
If the code is this:
arr=Array("a","b","c");
for(i in arr);
{
alert(i);
}
there is no any alert,but if it is this:
arr=new Array("a","b","c");
for(i in arr)
{
alert(i);//alerts 0,1,2
}
What is the reason?
Array is a constructor. To create a new object you should be using the new operator to create the object, which the constructor is then bound to and run against. In this case though, it actually should work either way, your issue is most likely related to the semicolons next to your for-loop, as noted in the comments.
As an aside, for creating a new array its generally advised to use the simpler notation
var arr = ["a","b","c"];
Its also questionable to use a for-in loop with an array in javascript, as that will hit any additional properties defined on the array. (so if you said arr.x = 2 it would also iterate over x.
Better to use the iterative form
var i =0, length =arr.length;
for ( ;i<length; i++) {
alert(arr[i]);
}
The reason you're getting different results is that you were using incorrect syntax for your for/in loops.
for(i in arr);
{
alert(i);
}
should not have the first semicolon.
Also note that a nicer way to iterate over an array would be:
arr.forEach(function(value, index){
alert(value); // or alert(index);
});
As bfavaretto has mentioned.
Invoking the Array function without the new keyword will Create and return a new Array object in the same way it would had you used the new keyword.
So these two will alert the same things:
arr1 = new Array("a","b","c");
arr2 = Array("a","b","c");