How to convert Javascript Array to array of functions? - javascript

I had a question in my interview where they asked me to transform an array to array of functions like below:
var a = ["a", 24, { foo: "bar" }];
var b = transform(a);
console.log(a[1]); // 24
console.log(b[1]()); // 24
I tried many combinations but I couldn't really do what they ask. Can someone tell me how to implement the transform function?

I'm not exactly sure about their intention, but a simple approach to provide the given results would be as follows:
function transform(a){
return a.map( (el) => () => el );
}
PS: the interesting follow up question would then be about the differences between the results for b[0]() and b[1]() compared to b[2]() and how to mitigate the problems that might arise for the later.

The desired behavior is a bit weird but you can do as follow:
const transform = arr => arr.map(elmt => () => elmt)

You can use a nice feature here, Proxy...
var arr = ["a", 24, { foo: "bar" }];
function transform(arr){
return new Proxy(arr, {
get: (obj, prop) => { return ()=>obj[prop] }
});
}
var newArr = transform(arr);
newArr[0]();

One approach you could take is to call Array.prototype.map method on your original array a - the map method will return a new array. Each index of the new array will have get a value of an anonymous function definition that, when called, will return the value of the corresponding index in the original array.
var a = ["a", 24, {
foo: "bar"
}];
var b = transform(a);
console.log(a[1]); // 24
console.log(b[1]()); // 24
function transform(array) {
return array.map(function(el) {
return function() {
return el;
}
});
}

Related

Oh man groupBy() and arrayToObject() are breaking me

So I have been working on some extra credit for my classes. I am very new to programming and have already sought help for this same assignment, I started rolling through and now am absolutely lost.
I need to define the two functions groupBy() and arrayToObect() as asked in the below test.
I'm not necessarily looking for the answer but if someone could help point me in the right direction that would be awesome.
What I have deduced is as follows:
I need to be using the spread operator ...
I need to create a newObj = {}
a. and somehow push the element derived from the array into the obj
I need to take the individual values of the array and assign them as keys, with the variances as the properties of the key.
Bracket notation
I have been racking my brain for hours on this now and could really use some guidance!
describe('groupBy', function () {
const input = [4.2, 6.1, 6.3]
const result = groupBy(input, (el) => Math.floor(el))
it('returns an object', function () {
expect(result).to.be.an('object')
})
it('group array items together based on the callback return value', function () {
expect(result).to.be.eql({
4: [4.2],
6: [6.1, 6.3],
})
})
})
describe('arrayToObject', function () {
const input = ['cat', 'dog', 'bird']
const result = arrayToObject(input, (word) => word + 's')
it('returns an object', function () {
expect(result).to.be.an('object')
})
it('object has original array elements as keys and the result of the callback as values', function () {
expect(result).to.be.eql({
cat: 'cats',
dog: 'dogs',
bird: 'birds',
})
})
})
})
groupBy
Write a function called groupBy which takes an array and a callback. The function should return an object. Each return value of the callback should be a key of the object and the values should be the input element with which the callback was called.
arrayToObject
Write a function called arrayToObject which takes an array and a callback. The function should return an object. Each element of the input array should be a key of the returned object and the output from the callback with an element passed in as the corresponding value.
These questions have been answered a million times on stackoverflow. Essentially what you want to be doing here is using the common js array functions map, filter, reduce, flatten, ..., and think about how your problem can be expressed in terms of those.
A lot of real world code is transforming data like this, so it's good to be comfortable doing it.
Also realize that spread syntax copies the entire object which can be pretty inefficient. JavaScript doesn't have persistent data structures! It's usually better to just mutate — as long as your code is what "owns" the object.
const groupBy = (elts, keyfn) =>
elts.reduce((m, elt) => {
const key = keyfn(elt);
m[key] = m[key] || [];
m[key].push(elt);
return m;
}, {});
const arrayToObject = (elts, fn) =>
elts.reduce(
(obj, elt) => Object.assign(obj, { [elt]: fn(elt) }),
{},
);
I figured it out using a for loop!!
function groupBy(arr, callback) {
const newObj = {}
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
const key = callback(arr[i])
newObj[key] = newObj[key] || []
newObj[key].push(arr[i])
}
}
return newObj
}
function arrayToObject(arr, callback) {
const obj = {}
for (let i = 0; i < arr.length; i++) {
if (callback(arr[i])) {
const key = callback(arr[i])
obj[arr[i]] = obj[key] || callback(arr[i])
}
}
return obj
}

JS How to make a new method to an Array using prototype but with a function that takes no parameters

I want to extend the Array with a new method called square() which returns a new array with all the numbers squared. I tried making it but I can't figure out a way in which the function does not take any parameters like the default JS Array methods. For example array.reverse() returns the array reversed it doesn't take the array as the parameter, like this: array.reverse(array)
This is my code:
Array.prototype.square = function(Array){
let a = []
for (let num of Array){
a.push(num**2)
}
return a
}
You can use the this keyword inside of your function and it will refer to the array calling it.
Array.prototype.square = function() {
return this.map(number => number ** 2)
}
let test = [1, 2, 3]
console.log(test.square())
You were on the right track, it can be easily done like this:
Array.prototype.square = function () {
return this.map((number) => number * number)
}
let a = [1, 2]; // sample array
console.log(a.square()); // prints [1, 4]
I have used map, which makes the process extremely easy. Refer to this for more information: Array Map Function
for the record...
(the name of this kind of added method is called a wrapper)
/* --- Array.square wrapper--- */
if (!Array.prototype.square) // check that the square method does not already exist
{
Array.prototype.square = function(){ return this.map(x=>x**2) }
}
let arr1 = [1,2,3,5,7]
, arr2 = arr1.square()
;
console.log('arr1 ->', JSON.stringify( arr1 ))
console.log('arr2 ->', JSON.stringify( arr2 ))
When you add methods to a prototype the object/array will always be the this context. So you can simply loop over this.
(Aside: it's often good to check that the method doesn't already exist on the prototype which is why I included that code too.)
if (!('square' in Array.prototype)) {
Array.prototype.square = function() {
const arr = [];
for (let i = 0; i < this.length; i++) {
arr.push(this[i] ** 2);
}
return arr;
}
}
console.log([1, 2, 3].square());
Or, more simply, use map to return a new array.
if (!('square' in Array.prototype)) {
Array.prototype.square = function() {
return this.map(el => el ** 2);
}
}
console.log([1, 2, 3].square());

How to get key values from nested JSON?

I have this JSON:
var json = [{'range':'2012','subtotal':'116.0','total_tax':'11.6','total':'127.6'},{'range':'2013','subtotal':'936.0','total_tax':'93.6','total':'1029.6'},{'range':'2014','subtotal':'368.0','total_tax':'36.8','total':'404.8'},{'range':'2015','subtotal':'267.0','total_tax':'26.7','total':'293.7'}];
How can I convert this into an array of ranges like this (using Javascript or jQuery):
['2012', '2013', '2014', '2015']
Thanks for any help.
You could simply use .map:
json.map(function(i) {
return i.range;
});
//["2012", "2013", "2014", "2015"]
First, that's not JSON, that's a JavaScript object literal. I'll refer to your data as data instead of json.
Second, let's use two reusable functions to express our intent more clearly
let map = f => x => x.map(f);
let prop = y => x => x[y];
Now, we simply map over your data and extract the property we want
map(prop('range'))(data);
// => ["2012","2013","2014","2015"]
Or we can define a reusable helper
let getRange = map(prop('range'));
getRange(data);
// => ["2012","2013","2014","2015"]
Here's the ES5 equivalent
var map = function map(f) {
return function (x) {
return x.map(f);
};
};
var prop = function prop(y) {
return function (x) {
return x[y];
};
};
var getRange = map(prop('range'));
getRange(data);
// => ["2012","2013","2014","2015"]
var mapped = json.map(function(obj) {
return obj.range;
});
Also, minor point; what you have is an array. JSON is when an array/object is represented as a string (which happens to match the JavaScript literal notation of it)
You actually have an array of nested objects. Here are some options:
The naive solution would be to iterate over this array and push to a new array the values you want:
function getRange(arr) {
var result = [];
for (var i = 0; i < arr.length; i++) {
result.push(arr[i].range)
};
return result;
}
You can also use the native Array.map method to do this easier:
function getRange(arr) {
return arr.map(function(elem) {
return i.range;
})
}

Get array from variable in javascript

I'm looking to get an array from a variable. If it's not already an array I want to return a new array with the variable as the only entry.Example:
toArray('test'); // => ["test"]
toArray(['test']); // => ["test"]
My actual working code is:
var toArray;
toArray = function(o) {
if (Array.isArray(o)) {
return o.slice();
} else {
return [o];
}
};
I'd like to know if there is a nicer way for that (native or with underscore.js for example).
In ruby, you can do:
Array('test') # => ["test"]
Array(['test']) # => ["test"]
Just use .concat().
[].concat("test"); // ["test"]
[].concat(["test"]); // ["test"]
Arrays will be flattened into the new Array. Anything else will simply be added.
function toArray(o) {
return Array.isArray(o) ? o.slice() : [o];
};
I believe you can slice it:
var fooObj = {0: 123, 1: 'bar', length: 2};
var fooArr = Array.prototype.slice.call(fooObj);
Demo: http://jsfiddle.net/sh5R9/

using underscore's “difference” method on arrays of objects

_.difference([], [])
this method works fine when i'm having primitive type data like
var a = [1,2,3,4];
var b = [2,5,6];
and the _.difference(a,b) call returns [1,3,4]
but in case i'm using object like
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
doesn't seem to work
try this on for size for finding the difference of an array of objects:
var test = [{a: 1},{b: 2}];
var test2 = [{a: 1}];
_.filter(test, function(obj){ return !_.findWhere(test2, obj); });
While the accepted answer is correct, and the other answers give good ideas as well, there is an additional option that's pretty easy to implement with underscore.
This solution relies on each object having a unique ID, but in many cases this will be true, and you can get the difference of two arrays of objects in just two lines of code.
Using underscore's "pluck" method, you can quickly construct an array of all of the ID's in your source set and the target set. From there, all of underscore's array methods will work, difference, union, intersection etc...
After the operation, it is trivial to obtain the list of objects from your source list that you desire. Here's an example:
Verbose:
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var arr1 = _.pluck(a, "id");
var arr2 = _.pluck(b, "id");
var diff = _.difference(arr1, arr2);
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
or, more concisely:
var diff = _.difference(_.pluck(a, "id"), _.pluck(b, "id"));
var result = _.filter(a, function(obj) { return diff.indexOf(obj.id) >= 0; });
Of course, this same technique can be extended for use with any of the array methods.
Reason is simply that object with same content are not same objects e.g.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
a.indexOf({'id':1, 'value':10})
It will not return 0 but -1 because we are searching for a different object
See the source code http://underscorejs.org/underscore.js, _.difference uses _.contains
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
and _.contains ultimately uses indexOf hence will not find objects unless they point to same object.
You can improve the underscore _.contains by looping through all items and calling a compare callback, which you should be able to pass to difference or contains function or you can check this version which improves contains methods
without using underscorejs,
here is the pretty simple method i got solution ...
a = [{'key':'123'},{'key':'222'},{'key':'333'}]
b = [{'key':'123'},{'key':'222'}]
var diff = a.filter(function(item1) {
for (var i in b) {
if (item1.key === b[i].key) { return false; }
};
return true;
});
console.log('result',diff)
I actually can imagine situations where I'd rather use #kontr0l approach than something else, but you have to understand that this approach is quadratic, so basically this code is an abstraction for naïve approach - iterate through all values in two arrays.
There are approaches better than quadratic, I won't use here any big O notation, but here are two main approaches, both are better then naïve one:
iterate through one of the arrays and check for existence in sorted second array using binary search.
put values into set/hash/dictionary/you name it.
As it've been already mentioned, first approach can be adopted for objects if you reimplement standard difference method with using some more flexible analogue of indexOf method.
With second approach we can hit the wall with the fact that, as of Feb'2015, only modern browsers are supporting Sets. As of hashes (well, objects) in javascript, they can have only string-type keys, so any object invoked as key first shoud be converted via toString method. So, we need to provide some => correspondece. On practice in most cases it's pretty straightforward, for instance, for your particular example such correspondence can be just String(obj.id).
Having such correspondence, we also can use following lodas/undercore approach:
var idsA = _.pluck(a, 'id');
var idsB = _.pluck(b, 'id');
// actually here we can stop in some cases, because
// quite often we need to identify object, but not the object itself -
// for instance to send some ids through remote API.
var intersect = _.intersection(idsA, idsB);
//to be 100% sure you get the idea, here we assume that object having equal ids are treated as equal, so does not really matter which of arrays we'll iterate:
var dictA = _.object(idsA, a); // now we can find a by id faster then with _.find
var intersectObj = intersect.map(function(id) {return dictA[id})
But buy admitting slightly stricter restriction - that we can build correspondence between our set objects and natural numbers we can build even more efficent algorithm, i.e. all our ids are non-negative integers - we can use more efficient algorithm.
The trick is to implement set by introducing two helper arrays this way:
var naturalSet = function (arr) {
var sparse = [];
var dense = [];
var contains = function (i) {
var res = sparse[i] < dense.length && dense[sparse[i]] == i;
return res;
}
var add = function (v) {
if (!contains(v)) {
sparse[v] = dense.length;
dense.push(v);
}
}
arr.forEach(add);
return {
contains: contains,
toArray: function () {
return dense
},
_getDense: function () {
return dense
},
_getSparse: function () {
return sparse
}
}
}
Then we can introduce set with mapping to naturalSet:
var set = function (arr, valueOf) {
var natSet = naturalSet(arr.map(valueOf));
return {
contains: function (item) {
return natSet.contains(valueOf(item))
},
toArray: function () {
var sparse = natSet._getSparse();
var res = natSet._getDense().map(function (i) {
return arr[sparse[i]];
});
return res;
}
}
}
and finally, we can introduce intersection:
var intersection = function(arr1, arr2, valueOf) {
return set(arr2.filter(set(arr1, valueOf).contains), valueOf).toArray();
}
So, relying on the structure of data you are working can help you sometimes.
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
var c = _.difference(a.map(e => e.id), b.map(e =>e.id));
var array = [];
array = a.map(e => {
if(c.includes(e.id)){
return e;
}
}).filter(r=>r);
Don't get why these answers are so complex unless I'm missing something?
var a = [{'id':1, 'value':10}, {'id':2, 'value':20}];
var b = [{'id':1, 'value':10}, {'id':4, 'value':40}];
// Or use lodash _.differenceBy
const difference = (array1, array2, prop = 'id') =>
array1.filter(item1 =>
!array2.some(item2 =>
item2[prop] === item1[prop],
),
);
// In one array.
console.log(difference(a, b));
// Intersection.
console.log([...difference(a, b), ...difference(b, a)]);
Forgive me for hopping in late here, but this may help:
array_of_objects =
// return the non-matching items (without the expected properties)
_.difference(array_of_objects,
// filter original list for items with expected properties
_.where(
// original list
array_of_objects,
// expected properties
{'id':1, 'value':10}
)
)

Categories