How to Define Map in Terms of ForEach - javascript

I have defined my own map function, which serves the same purpose as the method on Array objects. (I am doing this to learn). The code is below.
I would like to use Array's forEach method inside of my own definition of map, but I am missing something conceptually. How would I be able to do this using forEach?
Working without using forEach
// transforming with map
// NOTE: map is already defined in JavaScript
function map(array, transformation) {
var mapped = [];
for (var index in array) {
var currentElement = array[index];
var transformedElement = transformation(currentElement);
mapped.push(transformedElement);
}
return mapped;
}
My Attempt of using forEach
function mapForEach(array, transformation) {
var mapped = [];
array.forEach(transformation(currentVal, index, array, mapped));
return mapped;
}
function transformation(person, index, array, accumulator) {
var name = person.name;
accumulator.push(name);
}

The missing concept here is pass by value. The changes to accumulator will not be reflected in forEach. forEach is not actually designed to return a value for each iteration. For that you have map.
Taken from here,
foreach iterates over a list and applies some operation with side
effects to each list member (such as saving each one to the database
for example)
map iterates over a list, transforms each member of that list, and
returns another list of the same size with the transformed members
(such as converting a list of strings to uppercase)
Try this code:
function mapForEach(array, transformation) {
var mapped = array.map(transformation);
return mapped;
}
function transformation(person, index, array) {
var name = person.name;
return name;
}
Here is a JSFiddle
If you absolutely have to use forEach, then rather than passing a value, a global variable has to be used. This would be then
var accumulator =[];
function mapForEach(array, transformation) {
array.forEach(transformation);
return accumulator;
}
function transformation(person, index, array) {
var name = person.name;
accumulator.push(name);
}
JSFiddle - forEach

You're almost right with your method, but like #Katana314 says, you need to bind, not call.
Here's how I would do it:
function map(array,mapper) {
var ret=[];
array.forEach((val,key)=>{
//Since arrays are supposed to be sequential, we don't have to worry
//about keys matching, if they don't match, the user/developer did
//something horribly wrong.
ret.push(mapper(val,key,array));
});
return ret;
}
And here's how I would fix your way.
function mapForEach(array, func) {
var mapped = [];
array.forEach(mapForEachPusher.bind(null,mapped,func));
return mapped;
}
function mapForEachPusher(mapped,func,value,index,array) {
mapped.push(func(value,index,array));
}
function extract_name(person) {
return person.name;
}
mapForEach(
[{name:'Billy'},{name:'Bob'},{name:'Bridget'},{name:'Brittany'},{name:'"B word"'}]
,extract_name
);
//["Billy", "Bob", "Bridget", "Brittany", "\"B word\""]
It's effectively the same thing, your way moves the .push() call out of the main function, which means you have to pass data around, whereas I only need to reference from the outer scope. The only downside to my way is the overhead of the function, but it is an arrow function, so that might make it lightweight enough to not matter.

Related

How to write a function that emulates _.each() from underscore.js

There is this exercise I cannot wrap my head around.
I need to write a function that emulates the underscore.js function _.each() in a way that produces the same result and also passes the following tests:
should iterate over an array
should iterate over an object
should ignore the object prototype
should access the original collection
should bind to context if one is passed
should return the collection
This is the empty given function:
_.each= function (collection, iteratee, context) {}
and this is what I have written so far and do not pass any of the tests:
_.each = function (collection, iteratee, context) {
if (Array.isArray(collection)) {
for (let key of collection) {
console.log(key, collection.keys());
}
} else {
for (let prop in collection) {
if (collection.hasOwnProperty(prop)) {
console.log(`${prop}: ${collection[prop]}`);
}
}
}
return collection
};
What I am trying to do here is to iterate through the array with for of loop, and through the object with for in loop ignoring the object prototype properties.
I actually do not want to have the solution of the problem nor specific code, just a push in the right direction, and then find the solution myself.
I am pretty new to JS and I admit I cannot really think of a way to face this problem. Any advice would be greatly appreciated.
Thanks
Here is a hint. I hope this pushes you in the right direction.
_.each = function (collection, iteratee, context) {
// you're going to want something that checks length of arguments and binds context to iteratee here
if (Array.isArray(collection)) {
for (let key of collection) { /* you're gonna need the index later so consider using
for (let i = 0; i < collection.length; i++) {...} */
console.log(key, collection.keys()); // replace this line with calling iteratee with item, i, and collection
}
} else {
for (let prop in collection) {
if (collection.hasOwnProperty(prop)) {
console.log(`${prop}: ${collection[prop]}`); // replace this line with calling iteratee with collection[prop], prop, and collection
}
}
}
return collection
};
Usually, the test for an Each implementation starts by creating an example function, and an example collection, then calling your function on them.
Example:
var array0 = [1,2,3];
var array1= [];
var iteratee = function (item) {
array1.push(item * 2);
}
When the function is called on each item, it will do the thing that has been specified. So, in this case, it pushes the result of multiplying each item by 2 into the empty array.
The expected output of the above example would be an array1 with [2,4,6] in it.
Since you haven't written the part when the iteratee is called, when the test runs, nothing happens.

forEach is not a function when trying to loop through Firebase result set:

I am trying to port some of my Firebase database calls to an IOT board that does not have jQuery, just good old JavaScript.
In my code I originally had a jQuery $.each(tripData, function(index, element)
... loop to iterate through my results.
I have switched this to:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var tripData = response.val();
tripData.forEach(function (index, element) {
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
});
... but, I am getting the following error:
forEach is not a function
I am not sure how to resolve this.
for(let index in tripData){
element = trimpData[index];
}
not realy foreach, but works exactly like it
but you also can use map functions
You should really figure out if your response is an Array or Object.
$.each() iterates over arrays AND objects, thats why it works.
you should use for...in statement if you really want to iterate over this object tripData.
for(let prop in tripData)
{
if (tripData.hasOwnProperty(index))
{
item = tripData[prop];
// do stuff
}
}
lear about for...in statement here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
While the answer by #TypedSource will iterate over the resulting children, the order in which it iterates is undetermined - and very likely not going to be by timestamp.
A snapshot that you get as the result for a query contains three pieces of information for each child: its key, its value, and its position relative to the other children. When you call .val() on the snapshot, you lose the relative ordering.
To maintain the order, use the built-in forEach() method of the snapshot:
var tripsRef;
tripsRef = firebase.database().ref('trips/');
tripsRef.orderByChild('timestamp').limitToLast(100).on('value', function (response) {
var index = 0;
response.forEach(function (child) {
var element = child.val();
if (element.status >= 0) {
var trip = new Object();
trip.id = index;
trip.launch = element.launch;
trip.status = element.status;
}
index++;
});
});
Array.of(response.val()).forEach should work if it is just an array-like object missing its iterator

Can a function receive an object as an argument in javascript?

var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
}
else
log.error("adklafjklajkl");
}
catch(ie){
log.error("couldn't set preset");
}
presets.forEach(function(obj));
I know there must be mistakes in this code that I wrote, first of all, I was told that the function need to receive an object as an argument, which I have no idea how to pass it to the function. I tried google, but I did not find any relevant information on whether a function can receive an object as an argument.
presets is an array which contains objects which has two properties (called "name" and "value")
basically, the array Presets goes through its enumerated list of variables with forEach, and compare if the argument obj's name and value are identical or not to any of the objects stored inside the array, if they are identical, set the currentbutton's name and value to the one inside the argument obj. Then we will have other functions which will operate on currentbutton that i don't have to worry about.
I know it's not really clear because I am not even sure if that's what is wanted of me.
You don't quite understand how forEach works. The forEach method takes a function as its argument:
[1,2,3].forEach(function(item) {
alert(item);
});
That function passed into forEach is given an argument itself. Here, I've named it item. The forEach method repeatedly invokes the function and supplies a successive member of the array as the first argument each time it is invoked.
Now, instead of passing in a literal function, I can use a variable to hold my function:
var alertStuff = function(item) {
alert(item);
}
Then, I use that function (referring to it by variable name) in forEach:
[1,2,3].forEach(alertStuff);
// is the same as...
[1,2,3].forEach(function(item) {
alert(item);
});
Thus, you want to use presets.forEach(setPreset);.
Define a function which accepts a paramter
function myNewFunc(obj){
alert(obj.myFirstProp);
}
Define an object which we are going to pass as an argument to the above function
var myObject = {
myFirstProp: "testing"
};
Call the function and pass the object as an argument
myNewFunc(myObject);
Your brackets were screwed up and you invoked forEach wrong.
var presets = [
{name:'a', value:1},
{name:'b', value:2},
{name:'c', value:3},
];
var currentbutton = {};
function setPreset(obj) {
try{
if(obj.name===name && obj.value===value){
//log.error("preset array's OID at position ["+index+"] is"+presets[index].name +" and the value stored is "+presets[index].value);
currentbutton.name=obj.name;
currentbutton.value=obj.value;
log.error("currentbutton name= "+currentbutton.name+ "currentbutton value= " + currentbutton.value );
} else { // syntax error, opening { of else block was missing
log.error("adklafjklajkl");
}
} // syntax error, closing } of try block was missing
catch(ie){
log.error("couldn't set preset");
}
} // syntax error, closing } of function was missiong
presets.forEach(setPreset);

Jquery functional programming style

Is there a more elegant way to write this kind of function without having to initialize an array:
function getStuff () {
var some_array= [];
$("#some-id ul li span.key").each(function() {
some_array.push($(this).text());
});
return some_array;
}
jQuery.map
function getStuff () {
return $.map($("#some-id ul li span.key"), function(el) {
return $(el).text();
});
}
Fiddle
Performance-wise, the difference is minimal. Also, as noted by Lix, choose the method which you find more readable and maintainable. In the end, both will end up creating an array, iterating over elements, pushing string values to the array and returning the array.
Just another more functional feeling way to go about this might be:
Define a "method" function:
var method = function (method) {
return function (item) {
return $(item)[method]();
};
};
You can use it like this:
var text = method('text');
var html = method('html');
var fadeIn = method('fadeIn');
To re-work your getStuff function:
var getText = function (arr) {
return $.map(arr, text);
};
$.map iterates through the array that was passed in, applying the function that was returned from var text = method('text'), which is just the execution of $(array item).method().
I just threw this together to demonstrate a way you can use these kinds of techniques. You would probably name things differently and define the "method" function as a custom library/helper that is meant to be used app-wide.

How do I pass an extra parameter to the callback function in Javascript .filter() method?

I want to compare each string in an Array with a given string. My current implementation is:
function startsWith(element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith);
This simple function works, but only because right now wordToCompare is being set as a global variable, but of course I want to avoid this and pass it as a parameter. My problem is that I am not sure how to define startsWith() so it accepts one extra parameter, because I dont really understand how the default parameters it takes are passed. I've tried all the different ways I can think of and none of them work.
If you could also explain how the passed parameters to 'built in' callback functions (sorry, I dont know of a better term for these) work that would be great
Make startsWith accept the word to compare against and return a function which will then be used as filter/callback function:
function startsWith(wordToCompare) {
return function(element) {
return element.indexOf(wordToCompare) === 0;
}
}
addressBook.filter(startsWith(wordToCompare));
Another option would be to use Function.prototype.bind [MDN] (only available in browser supporting ECMAScript 5, follow a link for a shim for older browsers) and "fix" the first argument:
function startsWith(wordToCompare, element) {
return element.indexOf(wordToCompare) === 0;
}
addressBook.filter(startsWith.bind(this, wordToCompare));
I dont really understand how the default parameters it takes are passed
There is nothing special about it. At some point, filter just calls the callback and passes the current element of the array. So it's a function calling another function, in this case the callback you pass as argument.
Here is an example of a similar function:
function filter(array, callback) {
var result = [];
for(var i = 0, l = array.length; i < l; i++) {
if(callback(array[i])) { // here callback is called with the current element
result.push(array[i]);
}
}
return result;
}
The second parameter of filter will set this inside of the callback.
arr.filter(callback[, thisArg])
So you could do something like:
function startsWith(element) {
return element.indexOf(this) === 0;
}
addressBook.filter(startsWith, wordToCompare);
For those looking for an ES6 alternative using arrow functions, you can do the following.
let startsWith = wordToCompare => (element, index, array) => {
return element.indexOf(wordToCompare) === 0;
}
// where word would be your argument
let result = addressBook.filter(startsWith("word"));
Updated version using includes:
const startsWith = wordToCompare => (element, index, array) => {
return element.includes(wordToCompare);
}
function startsWith(element, wordToCompare) {
return element.indexOf(wordToCompare) === 0;
}
// ...
var word = "SOMETHING";
addressBook.filter(function(element){
return startsWith(element, word);
});
You can use the arrow function inside a filter, like this:
result = addressBook.filter(element => element.indexOf(wordToCompare) === 0);
Arrow functions on MDN
An arrow function expression has a shorter syntax compared to function expressions and lexically binds the this value (does not bind its own this, arguments, super, or new.target). Arrow functions are always anonymous. These function expressions are best suited for non-method functions and they can not be used as constructors.
For anyone wondering why their fat arrow function is ignoring [, thisArg], e.g. why
["DOG", "CAT", "DOG"].filter(animal => animal === this, "DOG")
returns []
it's because this inside those arrow functions are bound when the function is created and are set to the value of this in the broader encompassing scope, so the thisArg argument is ignored. I got around this pretty easily by declaring a new variable in a parent scope:
let bestPet = "DOG";
["DOG", "CAT", "DOG"].filter(animal => animal === bestPet);
=> ["DOG", "DOG"]
Here is a link to some more reading:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#No_separate_this
based on oddRaven answer
and
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
i did it 2 different way .
1) using function way .
2) using inline way .
//Here is sample codes :
var templateList = [
{ name: "name1", index: 1, dimension: 1 } ,
{ name: "name2", index: 2, dimension: 1 } ,
{ name: "name3", index: 3, dimension: 2 } ];
//Method 1) using function :
function getDimension1(obj) {
if (obj.dimension === 1) // This is hardcoded .
return true;
else return false;
}
var tl = templateList.filter(getDimension1); // it will return 2 results. 1st and 2nd objects.
console.log(tl) ;
//Method 2) using inline way
var tl3 = templateList.filter(element => element.index === 1 || element.dimension === 2 );
// it will return 1st and 3rd objects
console.log(tl3) ;
There is an easy way to use the filter function, access all params, and not over complicate it.
Unless the callback's thisArg is set to another scope filter does not create its own scope, and we can access params within the current scope. We can set 'this' to define a different scope in order to access other values if needed, but by default it is set to the scope it's called from. You can see this being used for Angular scopes in this stack.
Using indexOf is defeating the purpose of filter, and adding more overhead. Filter is already going through the array, so why do we need to iterate through it again? We can instead make it a simple pure function.
Here's a use-case scenario within a React class method where the state has an array called items, and by using filter we can check the existing state:
checkList = (item) => { // we can access this param and globals within filter
var result = this.state.filter(value => value === item); // returns array of matching items
result.length ? return `${item} exists` : this.setState({
items: items.push(item) // bad practice, but to keep it light
});
}

Categories

Resources