Creating a simplified underscore _.invoke - javascript

I am trying to create underscore's _.invoke function. I can't figure out why I keep getting a TypeError, Cannot read property 'sort' of undefined. I assume this refers to the array being passed into the function, but I can log each array in the collection, so I don't know why undefined in being thrown up.
function each(collection, iteratee, context) {
let i
let boundIteratee = iteratee.bind(context)
if (Array.isArray(collection)) {
for (i = 0; i < collection.length; i++) {
boundIteratee(collection[i], i, context)
}
} else {
for (i in collection) {
if (collection.hasOwnProperty(i)) {
boundIteratee(collection[i], i, collection);
}
}
}
return collection
}
function map(collection, iteratee, context) {
let result = []
let formula = function(element, index) {
result.push(iteratee(element, index, context))
}
each(collection, formula, context)
return result
}
function invoke(collection, methodName) {
let args = Array.prototype.slice.call(arguments, 2)
let formula = function(array, index) {
//console.log(array) --> returns arrays in collection...
return methodName.apply(array, args)
}
return map(collection, formula)
}
function sortIt(array) {
return array.sort()
}
console.log(invoke([
[3, 1, 2],
[7, 6, 9]
], sortIt))

Depending on what you're trying to achieve, you can either replace your sortIt function with:
function sortIt() { return this.sort(); } // since you apply the arrays as context to the function
or replace
return methodName.apply(array, args);
with
return methodName(array);
Neither are ideal, though.
Also, please look up the apply() method.

Related

javascript - unused argument 'idtype' although called from a .map() function

I am trying to re-write a function that filters out a specific property of an object to a function that can be passed a property and filter it.
This is the initial function:
function filterCategory(xmlObject, id) {
let newData = [];
xmlObject
.Sports[0]
.Sport[0]
.Category
.map(function (category) {
if (category.$.CategoryID == id) {
newData.push(category);
}
});
xmlObject
.Sports[0]
.Sport[0]
.Category = newData;
return xmlObject;
}
This is my new function:
function filterProperty(xmlObject, property, idtype, id) {
let newData = [];
if(xmlObject.hasOwnProperty(property)) {
xmlObject.property.map(function(value) {
if(value.$.idtype == id) {
newData.push(value);
}
});
xmlObject.property = newData;
}
return xmlObject;
}
For the second function my linter returns Unused idtype. Will my function be able to access the argument, or will it fail because I am trying to call it from a map() function? If so, how can I avoid this?
If you want to use idtype as a dynamic object property, then you can't use it like my.object.idtype as that will look for the property on the object that is literally called "idtype", instead you can use bracket notation to access the property
value.$[idtype];
Further illustration:
var obj = { one: 1, two: 2, three: 'foobarbaz' };
function getThingFromObject(mything) {
return obj[mything];
}
console.log(getThingFromObject('one')); // 1
console.log(getThingFromObject('three')); // 'foobarbaz'

JavaScript: Creating a function to reject array elements found to be truthy, trouble with values in objects in the array

I'm working on a code challenge assignment. Create a function, reject, that takes an array and a callback function, and removes from the array any items that are found truthy when the callback function is run against them. I've written the following:
function reject(collection, callback) {
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
return collection;
}
and where I'm hitting a wall is a test with an array of key-value pairs. The failing test:
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
expect(evens).to.eql({b:2, d:4});
Lack of experience has exhausted my ability to search for answers effectively, so here we are. Any help or guidance is appreciated.
Edited to add:
Misread the tests in the original instructions (then failed to catch it when copy/pasting the test). I definitely know the difference between an object and an array, just thought I saw [{a:1, b:2, c:3, d:4}] in the document but it was actually ({a:1, b:2, c:3, d:4}) for whatever reason. Sorry.
I think this is what you're trying to do?
function reject(collection, callback) {
Object.keys(collection).forEach(function(key){
if(callback(collection[key], key, collection)){
delete collection[key];
}
});
return collection;
}
var obj = {a:1, b:2, c:3, d:4};
var isOdd = function(value, key, collection) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens); //prints { b: 2, d: 4 }
You have the right idea, however you need to look at the difference between a JavaScript object and array. Read this and learn the difference.
A JavaScript object does not have the property of .length to return the size of the collection. Use the following loop instead:
for (var key in collection)
A collection does not have the property .splice, that is for arrays. Instead of using .splice to remove the item use
delete collection[key]
Finally, pass the item in the collection to the callback
callback(collection[key])
Updated Answer:
function reject(collection, callback) {
for (var key in collection) {
if(callback(collection[key]) === true) {
delete collection[key];
}
}
return collection;
}
var obj = {a:1, b:2, c:3, d:4}; // Use for an object passed
var obj2 = [1, 2, 3, 4]; // Use as an array passed
var isOdd = function(value) { return value % 2 !== 0; };
var evens = reject(obj, isOdd);
console.log(evens);
// expect(evens).to.eql({b:2, d:4});
Here's the solution I ended up with. To clarify, the tests were passing in arrays and objects, so that's why I first had trouble (with the objects) and then there was some confusion in in the answers. I wrote:
function reject(collection, callback) {
if(Array.isArray(collection)){
for (var i = 0; i < collection.length; i++) {
if(callback(collection[i]) === true){
collection.splice(i, 1);
}
}
} else {
for (number in collection){
if(callback(collection[number]) === true){
delete collection[number];
}
}
};
return collection;
}
I know it could likely be much cleaner, but just for the sake of clarity, I wanted to show a solution that works.
I have created a not() function, which accepts a function and can be passed to a filter function:
// wraps passed function, so that when called,
// the return value will be negated
function not(a){
return function(){
return !a.apply(a, arguments);
}
}
Usage:
// some sample data
var animals = [
{name: 'Pete', type: 'fish'},
{name: 'Jim', type: 'fish'},
{name: 'Tom', type: 'cat'}
];
// a simple filter callback
var isCat = function(animal){
return animal.type === 'cat';
};
// gather all animals, which are not cats
var fishes = animals.filter(not(isCat));
I know it could likely be much cleaner
Just for the fun, here is how I alter the answer step by step to make it a bit cleaner.
Making it immutable
Changing the collection will change the original object, which is passed to the function,
since arrays and objects are reference types. To solve this, you can clone the collection and work
on that, or you can copy the elements, which are not rejected by the callback. I'm doing the latter.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
for (var i = 0; i < collection.length; i++) {
if(!callback(collection[i])){
ret.push(collection[i]);
}
}
} else {
ret = {};
for (number in collection){
if(!callback(collection[number])){
ret[number] = collection[number];
}
}
}
return ret;
}
Shortening with ES5
The loops' mechanics and the actual code done by the loop is entangled, we can have a much cleaner
code, if we stop concentrating on how to write a loop and let JS do it. For example: notice how
I refer to the individual elements of the array collection as value, instead of collection[i].
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = [];
collection.forEach(function(value){
if(!callback(value)){
ret.push(value);
}
});
} else {
ret = {};
Object.keys(collection).forEach(function(key){
var value = collection[key];
if(!callback(value)){
ret[key] = value;
}
});
}
return ret;
}
Changing if to filter()
Array.prototype.filter() is a bit more useful for us, than forEach, since in the core of the loop you can
simply return a truthy or falsy value and filter will handle collecting the data to a new array based on
that automatically for you.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = {};
Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).forEach(function(key){
ret[key] = collection[key];
});
}
return ret;
}
Using reduce for objects
The goal would be to minimize functions, which go outside from their scope in order to work correctly.
In the objects part we can use Array.prototype.reduce() instead of forEach() and simply return it's
output directly to the ret value, when we are done, just as we did in the Array part with filter().
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(function(value){
return !callback(value);
});
} else {
ret = Object.keys(collection).filter(function(key){
return !callback(collection[key]);
}).reduce(function(obj, key){
obj[key] = collection[key];
return obj;
}, {});
}
return ret;
}
Shortening functions with ES6
Since we are already using Array.isArray(), which is an ES6 method, we can try using arrow functions
to compress anonymus functions.
function reject(collection, callback) {
var ret;
if(Array.isArray(collection)){
ret = collection.filter(value => !callback(value));
} else {
ret = Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
;
}
return ret;
}
We don't need the ret variable
We previously removed the need to access the ret value in our logics, we can use a ternary operator
to directly return the value generated by the expressions.
function reject(collection, callback) {
return (
Array.isArray(collection)
? collection
.filter(value => !callback(value))
: Object.keys(collection)
.filter(key => !callback(collection[key]))
.reduce((obj, key) => {
obj[key] = collection[key];
return obj;
}, {})
)
}

Why would my find method return undefined?

I'm recreating a number of Underscore.js methods to study JavaScript and programming in general.
Below is my attempts to recreate Underscore's _.find() method.
var find = function(list, predicate) { // Functional style
_.each(list, function(elem){
if (predicate(elem)) {
return elem;
}
});
};
var find = function(list, predicate) { // Explicit style
if (Array.isArray(list)) {
for (var i = 0; i < list.length; i++) {
if (predicate(list[i])) {
return list[i];
}
}
} else {
for (var key in list) {
if (predicate(list[key])) {
return list[key];
}
}
}
};
My second find method, which is using for loop and for in loop works. Whereas, my first find method would return undefined. I believe both should do the same work. However, they don't. Would someone please point what is going on?
Your return is only returning from the inner (nested) function and your find function is indeed not returning anything, hence the undefined.
Try this instead:
var find = function(list, predicate) { // Functional style
var ret;
_.each(list, function(elem){
if (!ret && predicate(elem)) {
return ret = elem;
}
});
return ret;
};

_.each function for looping in JS issue

We are asked to do the following:
Write a function called checkValue that searches an array for a value. It takes an array and a value and returns true if the value exists in the array, otherwise it returns false.
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function(arr, val) {
//checks if the val is in arr
}
Rewrite checkValue using _.each.
here is what I have to itterate over helloArr using _.each:
var helloArr = ['bonjour', 'hello', 'hola'];
var checkValue = function (num) {
return num;
}
checkValue('hola');
var output = us.each(helloArr, function(num){
if (checkValue())
{return true;}});
return output;
What am I doing wrong? When I run it with node, theres no errors but no output either. I know you can use _.find to do this but the spec is asking to itterate over the array and find the value using _.each.
In your second example, you're calling checkValue without a parameter, so it's going to return undefined, which is a falsey value, and the callback to each never returns anything.
Then again, it doesn't normally need to return anything anyway. _.each returns the list it operates on.
_.each is like a for-loop; consider treating it more like one.
function checkValue_original1(arr, val) {
for (var i = 0; i < arr.length; i++) {
if (val == arr[i]) return true;
}
return false;
}
function checkValue_original2(arr, val) {
return arr.indexOf(val) >= 0;
}
function checkValue_us_each(arr, val) {
var found = false;
_.each(arr, function(element, index, list) {
if (element == val) found = true;
});
return found;
}

List data structures in JavaScript

In an exercise in the book Eloquent JavaScript I need to create a list data structure (as below) based on the array [1, 2, 3].
The tutorial JavaScript Data Structures - The Linked List shows how to do this, but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
var list = {
value: 1,
rest: {
value: 2,
rest: {
value: 3,
rest: null
}
}
};
I tried to solve this via the code below.
function arrayToList(array){
var list = { value:null, rest:null};
for(i=0; i<array.length-1; i++)
list.value = array[i];
list.rest = list;
return list;
}
This code gives me an infinite loop of array[0]. What's wrong with my code?
This tutorial shows how to do this but I don't really understand the intention to create this.start and this.end variables inside the tutorial.
The tutorial uses a List wrapper around that recursive structure with some helper methods. It says: "It is possible to avoid having to record the end of the list by performing a traverse of the entire list each time you need to access the end - but in most cases storing a reference to the end of the list is more economical."
This code gives me an infinite loop of array[0].
Not really, but it creates a circular reference with the line list.rest = list;. Probably the code that is outputting your list chokes on that.
What's wrong is with my code?
You need to create multiple objects, define the object literal inside the loop body instead of assigning to the very same object over and over! Also, you should access array[i] inside the loop instead of array[0] only:
function arrayToList(array){
var list = null;
for (var i=array.length-1; i>=0; i--)
list = {value: array[i], rest:list};
return list;
}
This particular data structure is more commonly called cons. Recursion is the most natural (not necessarily the most efficient) way to work with conses. First, let's define some helper functions (using LISP notation rather than "value/rest"):
function cons(car, cdr) { return [car, cdr] }
function car(a) { return a[0] }
function cdr(a) { return a[1] }
Now, to build a cons from an array, use the following recursive statement:
cons-from-array = cons [ first element, cons-from-array [ the rest ] ]
In Javascript:
function arrayToList(array) {
if(!array.length)
return null;
return cons(array[0], arrayToList(array.slice(1)));
}
And the reverse function is similarly trivial:
function listToArray(list) {
if(!list)
return [];
return [car(list)].concat(listToArray(cdr(list)));
}
function arrayToList (arr) {
var list = null;
for (var i = arr.length - 1; i >= 0; i--) {
list = {
value: arr[i],
rest: list
};
}
return list;
}
function prepend (elem, list) {
return {
value: elem,
rest: list
};
}
function listToArray (list) {
var arr = [];
for (var node = list; node; node = node.rest) {
arr.push(node.value);
}
return arr;
}
function nth(list, num) {
if (!list) {
return undefined;
} else if (num === 0) {
return list.value;
} else {
return nth(list.rest, num - 1);
}
}

Categories

Resources