Normally I don't have too much trouble figuring out a problem in JS but this time I really need some help understanding this block of code. Mary Rose Cook used this logic in her space invaders game to filter through the bodies array to find collisions with other bodies.
var bodies = [];
...
update: function () {
// bodies is an array of all bodies in the game
var bodies = this.bodies;
var notCollidingWithAnything = function (b1) {
return bodies.filter(function(b2) { return colliding(b1, b2); }).length === 0;
};
this.bodies = this.bodies.filter(notCollidingWithAnything)
// ...insert function to draw the bodies that are in the new bodies array...
}
Can someone please explain how this.bodies.filter(notCollidingWIthAnything) works without passing in any parameters to the argument function? How does the the compiler know to check each element of the array against each other element of the array? Please guide me through what exactly happens in the compiler so that I can understand this.
Can someone please explain how this.bodies.filter(notCollidingWIthAnything) works without passing in any parameters to the argument function? How does the the compiler know to check each element of the array against each other element of the array?
The compiler (well, the JavaScript engine) doesn't know how to call notCollidingWIthAnything with the elements; Array#filter does.
notCollidingWIthAnything is a reference to the function. (Functions are proper objects in JavaScript, so we have references to them just like we have references to other objects.) The code passes that reference into Array#filter, and then Array#filter calls that function once for each element in the array, passing in the element value (and index, and array; it passes three args although we usually only use the first). Then it uses the return value of the callback to decide whether to include the element in the new array it builds.
Here's simplified code for Array#filter so you can see what's going on:
function arrayFilter(callback) {
// Remember this is called with `this` referring to an array-like object
// Create a new, empty array for the result
var result = [];
// Loop through the items
for (var index = 0; index < this.length; ++index) {
// Get the value for this entry
var value = this[index];
// Call the callback
if (callback(value, index, this)) {
// Got a truthy return value, include the value in the result
result.push(value);
}
}
// Return the new array
return result;
}
Again, that's simplified, not perfectly correct; for the perfectly correct steps, see the algorithm in the spec.
Here's an example with logging showing exactly who's doing what:
function arrayFilter(callback) {
console.log("Starting arrayFilter");
var result = [];
for (var index = 0; index < this.length; ++index) {
var value = this[index];
console.log("arrayFilter calling callback with value " + value);
if (callback(value, index, this)) {
console.log("arrayFilter got truthy result, include the value");
result.push(value);
} else {
console.log("arrayFilter got falsy result, don't include the value");
}
}
console.log("arrayFilter done");
return result;
}
function isOdd(value) {
var retval = value % 2 == 1;
console.log("isOdd called with " + value + ", returning " + retval);
return retval;
}
var a = [1, 2, 3, 4, 5, 6, 7, 8, 9];
console.log("calling `arrayFilter` with `a` as `this`, using `isOdd` callback");
var odds = arrayFilter.call(a, isOdd);
console.log("Resulting array: ", odds);
Related
I am new about programming. I have some difficulties to undurstand function.
So I done practice. But, I don't know why use return.
My code work.
In this exemple :
the function name is reverse.
I return in my function an array [1, 2, 5, 8].
Console.log a new array named reversed.
function reverse(array) {
const reversed = [];
for (i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
console.log(reversed);
}
return reverse([1, 2, 5, 8]);
**
But why after validate exercice, it's say me false and send me this solution :**
function reverse(array) {
const reversed = [];
for (let i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
return reversed;
}
Thanks : )
I googling and use chat GPT to teach me why use return in my case. But, nothing about I can understand.. I need your help.
Thank all.
The variable reversed is defined inside the function body so it can only be accessed from inside of the function. In your code you tried to access the variable from outside of the function. return keyword is used inside the function to return a value, using return outside the function has no effect.
Some remarks:
The names reverse and reversed are very similar which can lead to confusion. I see it happening in another answer and in your comments. reverse is defined as a function, and reversed is a local variable that references an array. They are completely different objects.
In your code you have placed a return statement where it is not valid. A return statement can only occur inside a function body.
console.log(reversed) will output the result in the console window, but that is not what the purpose is of the exercise. Although console.log is very useful while you are debugging your code, it is not equivalent to a return value. The caller of your function will get as return value what the function has returned with a return statement, not what console.log has output.
Your code does not define i with let (or var) and so it is implicitly declared as a global variable. This is not good practice. Always define variables explicitly.
Your code calls the reverse function. This is what you typically would do to test your function, but in code challenges, this call is typically made by the person or framework that tests your implementation. And those tests will call it more than once, with different arrays as argument, often including boundary cases, like an empty array, or a very large array.
So when you debug code, you would call the function and print the value that it returns, like so:
function reverse(array) {
const reversed = [];
for (let i = array.length - 1; i > -1; i--) {
reversed.push(array[i]);
}
return reversed;
}
let result = reverse([1,2,3,4]);
console.log(result); // Should be [4,3,2,1]
result = reverse([1]);
console.log(result); // Should be [1]
result = reverse([]);
console.log(result); // Should be []
result = reverse(reverse([1,2,3,4])); // Reversing the result!
console.log(result); // Should be [1,2,3,4]
Maybe I should also mention that reverse is also a native method available for arrays. It will reverse the array itself instead of creating a new array (like above). If the purpose is to always create a new array, you can still make use of it: first create a copy of the given array, reverse the copy and return that:
function reverse(array) {
// The spread syntax creates a copy, and reverse is applied on the copy
return [...array].reverse();
}
let result = reverse([1,2,3,4]);
console.log(result); // Should be [4,3,2,1]
result = reverse([1]);
console.log(result); // Should be [1]
result = reverse([]);
console.log(result); // Should be []
result = reverse(reverse([1,2,3,4])); // Reversing the result!
console.log(result); // Should be [1,2,3,4]
Im trying to get a sum of array injected into a function that loops until all the values are added, the console.log right before the "return" logs the right value, meaning the code works, but when I try to use that function with any array it returns "undefined"...
var total = function(arr) {
console.log(arr);
if(arr.length > 1) {
var temp = []
for(var i=0, len=arr.length-1; i<len; i++) {
temp.push(arr[i] + arr[i+1]);
}
total(temp);
}
else {
console.log(arr.join()); // 48, exectly what I need
return parseInt(arr.join());
}
}
var sup = total([1,2,3,4,5]); // undefined
Not completely sure how to debug it..
If your arr.length is greater than one, you will invoke total with the temporary array, however, you don't do anything with this temporary array - you don't return it, or utilize it in any way, so the intermediate results are lost.
In addition - this is not a self invoking function; it is recursion.
This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I am kind of new to javascript and trying to understand some non trivial - at least so i hope :) things.
my question is general, but i have a specific example which can help me ask my question and help you understand what i mean.
the example:
function updateBookmark(bookmark){
var index = _.findIndex($scope.bookmarks, function(b){
return b.id == bookmark.id;
});
return index;
}
obviously the findIndex function is declared somewhere (in our case - lodash.js)
and it gets two parameters (at least two visible parameters: a data set, and a function)
first question:
in this example, what is b? how does b gets its value? i understand b is each of the data set's objects, but i mean - what is going behind the scenes here so b will be what it is??
second question:
the author chose to pass an anonymous function which equals b.id with bookmark.id,
i understand that he can use bookmark.id where he is using it, but how does findIndex has access to this bookmark?!
this function as i concluded earlier is declared somewhere else, does it get all the variables in the scope some how?
what is going on here?
Thanks in advance to responders and sorry for the messy question...
Jim.
If you rewrite some things, it becomes easier to understand.
Starting with the last portion:
// Q: "How does `findIndex`have access to `bookmark`"
_.findIndex(bookmarks, function (b) { });
// A: "It doesn't."
var bookmark = { id: 1 };
var bookmarks = [ /* ... */ ];
function compareWithBookmark( test ) {
return test.id === bookmark.id;
}
_.findIndex(bookmarks, compareWithBookmark);
As you can see, findIndex doesn't actually have any access to bookmark.
Rather, it has access to a function which it can pass a value to test, and that function will return whether that test passed or failed.
Under the covers of .findIndex or [].map or [].filter, they're all just taking a function, making a loop, passing each element into the function one at a time, and doing something with the return value.
function findIndex (array, test) {
var index = -1;
var i = 0;
var l = array.length;
var el;
var result;
for (; i < l; i += 1) {
el = array[i]; // <-- how `b` got its value
result = test(el, i, array); // <-- test(b)
if (!!result) {
index = i;
break;
}
}
return index;
}
The different functions would do different things with the results (map returns a new array which contains each result, filter returns an array where only !!result tests passed, et cetera), but all of them do this inner-looping.
This is also a pretty gross simplification of the looping structure and considerations, but it's exactly what's driving your expected behaviour.
Edit
Here is a full usage of the function I just defined, plus the array, plus the object I'm checking.
var bookmarks = [
{ id: 2 },
{ id: 3 },
{ id: 6 },
{ id: 14 }
];
var bookmark = { id: 3 };
function compareBookmarkIdTest (el) {
return el.id === bookmark.id;
}
var index = findIndex(bookmarks, compareBookmarkIdTest);
index; // 1
Hope that helps.
For education purposes, I was trying to re-create Underscore.js's _.reduce() method. While I was able to do this in an explicit style using for loops. But this is far from ideal because it mutates the original list that was supplied as an argument, which is dangerous.
I also realized that creating such method using functional programming style is harder, since it is not possible to explicitly set i value for looping.
// Explicit style
var reduce = function(list, iteratee, initial) {
if (Array.isArray(list)) {
var start;
if (arguments.length === 3) {
start = initial;
for (var i = 0; i < list.length; i++) {
start = iteratee(start, list[i], i);
}
} else {
start = list[0];
for (var i = 1; i < list.length; i++) {
start = iteratee(start, list[i], i);
}
}
}
if (list.constructor === Object) {
var start;
if (arguments.length === 3) {
start = initial;
for (var key in list) {
start = iteratee(start, list[key], key);
}
} else {
start = list[Object.keys(list)[0]];
// Delete the first property to avoid duplication.
delete list[Object.keys(list)[0]];
for (var key in list) {
start = iteratee(start, list[key], key);
}
}
}
return start;
};
What makes me struggle is that when my reduce() is supplied with an argument, initial, I need to subsequently either skip or remove the first element or property of the argument, list for the final value that will be returned. Because not doing so will double count the first element/property. I can't think of how I could do such thing when creating the function with functional programming style, with _.each() or forEach() involved.
This is my functional style of reduce() that is working partially. It works correctly when memo(initial value) is supplied, because I don't need to skip the first element/property. But it's not working correctly when memo is not supplied, because then I'm setting memo to either first element or property, and I should be able to skip it during the looping, which I don't know how.
// Functional style (not working without memo)
var reduce = function(list, iteratee, memo) {
var memo = memo || list[0] || list[Object.keys(list)[0]];
_.each(list, function(element, index, list){
memo = iteratee(memo, element, index, list);
});
return memo;
};
I spent quite a long time searching for answers to my question on Google. But wasn't able to find one. I would really appreciate your advice. Thanks.
Lastly, this is an additional code I came up with which does not work, but I think it should.
var reduce = function(list, iteratee, memo) {
var collection = list;
var accumulation;
_.each(collection, function(item){
if (arguments.length < 3) {
if (Array.isArray(collection)) {
accumulation = collection[0];
collection.shift();
accumulation = iteratee(accumulation, item);
} else {
accumulation = collection[Object.keys(collection)[0]];
delete collection[Object.keys(collection)[0]];
accumulation = iteratee(accumulation, item);
}
} else {
accumulation = memo;
accumulation = iteratee(accumulation, item);
}
});
return accumulation;
};
Here is the shortest version I could come up with.
_.reduce = function(list, iteratee, memo){
var memoUndefined = arguments.length < 3;
_.each(list, function(elem, index, list){
if(memoUndefined) {
memoUndefined = false;
memo = elem;
} else memo = iteratee(memo, elem, index, list);
});
return memo;
};
Reduce accepts three parameters: A collection (array or object), callback, and accumulator (this one is optional).
Reduce iterates through the collection, which invokes the callback and keeps track of the result in the accumulator.
If an accumulator is not passed in, we'll set it to the first element of the collection.
If an accumulator is available, we'll set the accumulator to be equal to the result of invoking the callback and passing in the current accumulator and the current element of the collection. Remember: Javascript executes its operations in right-to-left order, meaning the right side of the operator occurs first before it gets assigned to the variable on the left.
_.reduce = function(collection, callback, accumulator){
_.each(collection, function(elem){
return accumulator === undefined ? accumulator = collection[0] : accumulator = callback(accumulator, elem);
});
return accumulator;
};
First, you need a way to determine whether reduce received an initial memo value when you are inside the function you pass to _.each. You could do this a number of ways. One way is to simply set a flag based on the length of arguments. You need to do this outside the _.each call because the function you pass to _.each will have its own arguments object, masking the arguments object for reduce.
Using your code as a starting point:
var reduce = function(list, iteratee, memo) {
var considerFirst = arguments.length > 2;
var memo = memo || list[0] || list[Object.keys(list)[0]];
_.each(list, function(element, index, list){
if (index > 0 || considerFirst) {
memo = iteratee(memo, element, index, list);
}
});
return memo;
};
This still isn't quite right, though. We also need to update how you are defaulting memo. Currently, if memo receives a falsy value (e.g. 0), we still set it to the fist element in the list, but we don't set the flag indicating to ignore the first element. This means reduce will process the first element twice.
To get this right, you need to change how you are defaulting memo, setting it only if no argument is passed in. You could do something like this:
var reduce = function(list, iteratee, memo) {
var considerFirst = true;
if (arguments.length < 3) {
memo = list[0];
considerFirst = false;
}
_.each(list, function(element, index, list){
if (index > 0 || considerFirst) {
memo = iteratee(memo, element, index, list);
}
});
return memo;
};
This way, you only set memo if no argument was passed.
Note that you don't need to initialize memo with var. Having memo as a parameter does all the initialization you need.
Also note that I removed support for using reduce on a plain object. When you pass an object to _.each, the value of the index parameter is not a numerical index but the key for that entry, which may or may not be an integer. This does not play well with our index > 0 check to see if we are looking at the first entry. There are ways around this, but it doesn't seem central to your question. Check out the actual underscore implementation if you want to see how to make it work.
Update: the implementation SpiderPig suggests doesn't rely on index and so would work with objects, not just arrays.
Lastly, it's worth pointing out that underscore's implementation of _.reduce uses a for loop and not _.each.
I have an array of objects. Each object has, among others, an ID attribute. I want to find the index in the array of the object with a specific ID. Is there any elegant and simple way to do this in jQuery?
See [`Array.filter`][1] to filter an array with a callback function. Each object in the array will be passed to the callback function one by one. The callback function must return `true` if the value is to be included, or false if not.
var matchingIDs = objects.filter(function(o) {
return o.ID == searchTerm;
});
All objects having the ID as searchTerm will be returned as an array to matchingIDs. Get the matching element from the first index (assuming ID is unique and there's only gonna be one)
matchingIDs[0];
[1]: https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Array/filter
Update:
Checkout findIndex from ECMAScript 6.
items.findIndex(function(item) { item.property == valueToSearch; });
Since findIndex isn't available on most browsers yet, you could backfill it using this implementation:
if (!Array.prototype.findIndex) {
Array.prototype.findIndex = function(predicate) {
if (this == null) {
throw new TypeError('Array.prototype.findIndex called on null or undefined');
}
if (typeof predicate !== 'function') {
throw new TypeError('predicate must be a function');
}
var list = Object(this);
var length = list.length >>> 0;
var thisArg = arguments[1];
var value;
for (var i = 0; i < length; i++) {
value = list[i];
if (predicate.call(thisArg, value, i, list)) {
return i;
}
}
return -1;
};
}
In the case you should use for loop in javascript instead of using jQuery. See way 3 in http://net.tutsplus.com/tutorials/javascript-ajax/10-ways-to-instantly-increase-your-jquery-performance/
UPDATED: jQuery is written in javascript and it can not be faster than another code written also in javascript. jQuery is very good if you work with the DOM, but doesn't really help if you're working with simple javascript arrays or objects.
The code you're looking for can be something like this:
for (var i=0, l = ar.length; i<l; i++) {
if (ar[i].ID === specificID) {
// i is the index. You can use it here directly or make a break
// and use i after the loop (variables in javascript declared
// in a block can be used anywhere in the same function)
break;
}
}
if (i<l) {
// i is the index
}
Important that you should hold some simple javascript rules: Always declare local variables (don't forget var before variable declaration) and cache any properties or indexes that you use more than one time in a local variable (like ar.length above). (See for example http://wiki.forum.nokia.com/index.php/JavaScript_Performance_Best_Practices)
Not really elegant, but a cute trick:
var index = parseInt(
$.map(array, function(i, o) { return o.id === target ? i : ''; }).join('')
);
jQuery doesn't have a lot of functional constructs like that; the philosophy of the library is really focused on the job of DOM wrangling. They won't even add a .reduce() function because nobody can think of a reason it'd be useful to the core functionality.
The Underscore.js library has a lot of such facilities, and it "plays nice" with jQuery.
There are no built-in methods for this; the [].indexOf() method doesn't take a predicate, so you need something custom:
function indexOf(array, predicate)
{
for (var i = 0, n = array.length; i != n; ++i) {
if (predicate(array[i])) {
return i;
}
}
return -1;
}
var index = indexOf(arr, function(item) {
return item.ID == 'foo';
});
The function returns -1 if the predicate never yields a truthy value.
Update
There's Array.findIndex() that you could use now:
const arr = [{ID: 'bar'}, {ID: 'baz'}, {ID: 'foo'}];
const index = arr.findIndex(item => item.ID === 'foo');
console.log(index); // 2
Use jOrder. http://github.com/danstocker/jorder
Feed your array into a jOrder table, and add an index on the 'ID' field.
var table = jOrder(data)
.index('id', ['ID']);
Then, get the array index of an element by:
var arrayidx = table.index('id').lookup([{ ID: MyID }]);
If you want the entire row, then:
var filtered = table.where([{ ID: MyID }]);
Voila.