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

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
}

Related

ES6 Filtering objects by unique attribute

I've an array of errors, each error has a non-unique param attribute.
I'd like to filter the array based on whether the param has been seen before.
Something like this:
const filteredErrors = [];
let params = [];
for(let x = 0; x < errors.length; x++) {
if(!params.includes(errors[x].param)) {
params.push(errors[x].param);
filteredErrors.push(errors[x]);
}
}
But I've no idea how to do this in ES6.
I can get the unique params const filteredParams = Array.from(new Set(errors.map(error => error.param)));
but not the objects themselves.
Pretty sure this is just a weakness in my understanding of higher order functions, but I just can't grasp it
You could destrucure param, check against params and add the value to params and return true for getting the object as filtering result.
As result you get an array of first found errors of the same type.
const
params = [],
filteredErrors = errors.filter(({ param }) =>
!params.includes(param) && params.push(param));
Instead of an array you can make use of an object to keep a map of existing values and make use of filter function
let params = {};
const filteredErrors = errors.filter(error => {
if(params[error.param]) return false;
params[error.param] = true;
return true;
});
i'd probably do it like this with a reduce and no need for outside parameters:
const filteredErrors = Object.values(
errors.reduce((acc, val) => {
if (!acc[val.param]) {
acc[val.param] = val;
}
return acc;
}, {}))
basically convert it into an object keyed by the param with the object as values, only setting the key if it hasn't been set before, then back into an array of the values.
generalized like so
function uniqueBy(array, prop) {
return Object.values(
array.reduce((acc, val) => {
if (!acc[val[prop]]) {
acc[val[prop]] = val;
}
return acc;
}, {}))
}
then just do:
const filteredErrors = uniqueBy(errors, 'param');
If your param has a flag identifier if this param has been seen before then you can simply do this.
const filteredErrors = errors.filter(({ param }) => param.seen === true);
OR
const filteredErrors = errors.filter((error) => error.param.seen);
errors should be an array of objects.
where param is one of the fields of the element of array errors and seen is one of the fields of param object.
You can do it by using Array.prototype.reduce. You need to iterate through the objects in the array and keep the found params in a Set if it is not already there.
The Set.prototype.has will let you find that out. If it is not present in the Set you add it both in the Set instance and the final accumulated array, so that in the next iteration if the param is present in your Set you don't include that object:
const errors = [{param: 1, val: "err1"}, {param: 2, val: "err2"}, {param: 3, val: "err3"}, {param: 2, val: "err4"}, {param: 1, val: "err5"}];
const { filteredParams } = errors.reduce((acc, e) => {
!acc.foundParams.has(e.param) && (acc.foundParams.add(e.param) &&
acc.filteredParams.push(e));
return acc;
}, {foundParams: new Set(), filteredParams: []});
console.log(filteredParams);

Find by value in JavaScript object

I'm struggling with some task in javascript. I have the following object:
const methods = {
searchRanking: {
id: 'tmEM6RoEPmUO',
template: 'main_template',
startUrl: 'https://www.amazon.de/s?k={value}',
startValueOverrideName: 'url',
dataProcessor: function (data) {
return data.map(/*doe shit met data*/)
}
},
productDetails: {
id: 'tAewRCbTuuL5',
template: 'main_template',
startUrl: 'https://www.amazon.de/dp/{value}',
startValName: 'url',
dataProcessor: function (data) {
return data.map(/*doe shit met data*/)
}
}
}
Every object has a processor method. I want to return the processor of the object that contains the id tmEM6RoEPmUO
I can loop through the keys and check for every key if it contains the given ID, but it seems like a workaround, is there a more efficient way?
I would simply loop through the keys and check. In that case time complexity will be O(N). It will be like
let res = Object.values(obj).find(x => x.id === 'tmEM6RoEPmUO').dataProcessor()
But consider that you want an O(1) time complexity then you can create another object with keys equal to id of the object.
let newObj = Object.fromEntries(Object.entries(obj).map(([_, v]) => [v.id, v]));
let res = newObj['tmEM6RoEPmUO'].dataProcessor()
You can pull the desired object out into a constant, method and invoke its dataProcessor method with your arguments like so:
const method = Object.values(obj).find(o => o.id === 'tmEM6RoEPmUO');
method.dataProcessor(data);
loop through all the key in object and check its id
for(let m of Object.keys(methods)){
if(methods[m].id == "tmEM6RoEPmUO"){
return methods[m].dataProcessor;
}
}

How to convert Javascript Array to array of functions?

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;
}
});
}

Quickest way to check which property inside an object holds value?

Probably a dumb question, Assume i have an object like below,
{"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""}"
What is the quickest way to check which properties hold the values in the above object without using a loop?
while doing this inside a for loop is working,
if(typeof someUndefVar == whatever) -- works
EXPECTED OUTPUT:
Country, Continent and State
var a = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
Object.keys(a).filter( prop => a[prop] );
It also depends on how you want to handle the 0, null, undefined values.
You might do as follows;
var obj = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
for (var prop in obj) !!obj[prop] && console.log(prop);
You need to use the object.keys() function,
Like this,
var jsonData= {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""};
console.log(Object.keys(jsonData));
Hope this helps!
Sorry you said no loop and I got excited and I was bored and I got functional.
First I shortened some names and I guess they are kind of self explanatory:
head: first element of an array
tail: all the elements but the first
isEmpty: check if an array has length 0
transform does the actual work using a callback, it stops if the keys array is empty if not goes recursive.
var data = {"Country":"country","Continent":"continent","Province":"","District":"","State":"state","City":""}
// plumbing
var head = (a) => a[0]
var tail = (a) => a.slice(1)
var isEmpty = (a) => a.length === 0
// actual stuff happens here
var transform = (obj, callback) => {
var withval = (keys) => {
if(isEmpty(keys)) return
var p = head(keys)
if(!!obj[p]) callback(p)
withval(tail(keys))
}
withval(Object.keys(obj))
}
// Logs
var log = (p) => console.log(p)
transform(data, log)
// Array making
var arr = [];
var toArr = (p) => arr.push(p)
transform(data, toArr)
console.log(arr)
// Object making
var obj = {}
var toObj = (p) => obj[p] = data[p]
transform(data, toObj)
console.log(obj)
Outcome:
Country
Continent
State
[ 'Country', 'Continent', 'State' ]
{ Country: 'country', Continent: 'continent', State: 'state' }

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;
})
}

Categories