Why does javascript reduce function output an array of strings - javascript

I am trying to sum up the numbers inside an array e.g. from here: https://jsfiddle.net/4r8dtxhz/12/
here is the Code:
var someObj = [{name:"hi", series: [1,2,10,4,5,6]},{name:"ho",series:[3,7,6,9,12,1,3,20,3,1]}]
for (var doc of someObj) {
this.min = doc.series.reduce((agg,val) => val < agg? val:agg, doc.series[0]);
this.max = doc.series.reduce((agg,val) => val > agg? val:agg, doc.series[0]);
}
console.log(max)
var test = Array.from(someObj.map((doc)=>doc.series)).reduce((accumulator, currentValue) => accumulator + currentValue);
console.log(typeof test)
console.log(test)
I was expecting the reduce function to sum up the numbers in the object series array... so I am wondering what goes wrong here?

Your map function is producing an two dimensional array of [someObj[0].series, someObj[1].series].
When you add two arrays together using the + operator in your reducer, it converts them to a string and concatenates the string.
If you want to create an array of the sum of each series, introduce another map function which has a reduce inside it.

You are missing a step to flatten the result of your map step. Your code
someObj.map((doc) => doc.series)
will return an array of arrays (or 2D array) rather than a flat array.
If you add a step to flatten the 2D array after your map step—for example by
.reduce((flattened, series) => flattened.concat(series))
using Array.reduce again—you will get the expected result.
Note that you should always provide an initial value for the accumulator of reduce (in your case 0 was missing for the summation) to assure that + is getting resolved correctly to number addition (otherwise it will be string concatenation, e.g. [1]+[2] === '12').
Also, Array.from wasn't necessary since someObj already is an array (Array.from converts from an Iterable object to Array).
var someObj = [{name:"hi", series: [1,2,10,4,5,6]},{name:"ho",series:[3,7,6,9,12,1,3,20,3,1]}]
for (var doc of someObj) {
this.min = doc.series.reduce((agg,val) => val < agg? val:agg, doc.series[0]);
this.max = doc.series.reduce((agg,val) => val > agg? val:agg, doc.series[0]);
}
console.log(max)
var test = someObj.map((doc)=>doc.series)
.reduce((flattened, series) => flattened.concat(series))
.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(typeof test)
console.log(test)

Related

Get the object that has a certain number in it from an object within an object

I make an API call and it gives back the following:
[
[1529539200,15.9099,16.15,15.888,16.0773,84805.7,1360522.8],
[1529625600,16.0768,17.38,15.865,17.0727,3537945.2,58937516],
[1529712000,17.0726,17.25,15.16,15.56,3363347.2,54172164],
[1529798400,15.55,16.0488,15.3123,15.6398,2103994.8,33027598],
[1529884800,15.6024,15.749,13.3419,14.4174,3863905.2,55238030],
[1529971200,14.4174,15.1532,13.76,14.8982,2266159.8,33036208],
...
]
There are basically about 1000 objects in total, and every object has 7 objects within it, each of them containing the values shown above. Right now I have set
var objects= response.data.result[86400]
which gives the result you see above, and now, I need to search through these objects until Javascript finds the object that has the value '1529884800' in object zero, so for example with the code above this would result in this number:
object[5][0]
I wrote the following ode but it doesn't work, results in an empty array as response.
var results = [];
var toSearch = 1529539200;
for (var i=0; i<objects.length; i++) {
for (key in objects[i][0]) {
if (objects[i][key].indexOf(toSearch) != -1) {
results.push(objects[i]);
}
console.log(results)
}
}
(in the above results just shows [])
I tried doing var toSerach ='1529539200' and without quotes but neither work, what is the issue here? Would appreciate any help, thanks
If you want the index of a given number, use .flatMap() and .indexOf()
First iterate through the outer array
array.flatMap((sub, idx) =>
Then on each sub-array find the index of the given number. .indexOf() will either return the index of the number if it exists or -1 if it doesn't. In the final return it will be the index number of the sub-array and then the index of the number within the sub-array if found. Otherwise an empty array is returned which results in nothing because .flatMap() flattens arrays by one level.
sub.indexOf(number) > -1 ? [idx, sub.indexOf(number)] : [])
const data = [[1529539200,15.9099,16.15,15.888,16.0773,84805.7,1360522.8],[1529625600,16.0768,17.38,15.865,17.0727,3537945.2,58937516],[1529712000,17.0726,17.25,15.16,15.56,3363347.2,54172164],[1529798400,15.55,16.0488,15.3123,15.6398,2103994.8,33027598],[1529884800,15.6024,15.749,13.3419,14.4174,3863905.2,55238030],[1529971200,14.4174,15.1532,13.76,14.8982,2266159.8,33036208]];
let A = 1529539200;
let B = 33036208;
let C = 15.16;
const findNumber = (array, number) =>
array.flatMap((sub, idx) =>
sub.indexOf(number) > -1 ? [idx, sub.indexOf(number)] : [])
console.log(findNumber(data, A));
console.log(findNumber(data, B));
console.log(findNumber(data, C));

Understanding a range function in JavaScript

So, I'm new to programming and I am having a bit of a trouble here. See, I wanted to write a small range function, like range(a,b) that would return an array with all numbers between a and b. So I googled and found this one:
const range = (min, max) => [...Array(max - min + 1).keys()].map(i => i + min);
This works perfectly fine, but I'm having a bit of trouble undertanding it, especially with the .keys() part. I thought .keys() was an Object function, that would return the key of a key/value pair of an object, but here it seems to me that it's been used in an Array.
What am I understanding wrong here?
Appreciate the help!
Arrays also have a keys method and it's central to this range function working as it should.
Here's how that works:
It creates an empty (sparse) array of the appropriate length (Array(max - min + 1)).
It gets an iterator for the valid indexes of that array (.keys()). Valid indexes in an arrays are the numbers 0 through the length of the array minus one. (Technically, in specification terms, the indexes of arrays are string property names like "0", "1", etc., but the keys iterator returns numbers because we typically use numbers to index into arrays and that's the more useful value to provide.)
It spreads the elements from that iterator out into an array ([...]).
It creates a new array by mapping each value in the array from #3, adding the min value to them (.map(i => i + min)).
Note that this creates multiple intermediate arrays and other objects. It's unlikely to matter, but it can be done much more efficiently. For instance, with Array.from as shown by Nina, or even just a simple loop:
const range = (min, max) => {
const result = [];
for (let n = min; n < max; ++n) {
result.push(n); // Or: `result[result.length] = n;` if you want to
// avoid the function call
}
return result;
};
const range = (min, max) => {
const result = [];
for (let n = min; n < max; ++n) {
result.push(n);
}
return result;
};
console.log(range(5, 10));
That's not nearly as cool-looking, though. :-D
But, I probably wouldn't have range return an array at all unless I know for sure that's the end product I'm always going to need. Instead, I'd have it return an iterator:
const range = function*(min, max) {
for (let n = min; n < max; ++n) {
yield n;
}
};
for (const value of range(0, 10)) {
console.log(value);
}
.as-console-wrapper {
max-height: 100% !important;
}
You can always spread it out into an array (or use it with Array.from) if you need an array.
Arrays have keys as well and returns an iterator for returning keys. This requires a spreading into an array or use a function which takes iterables and returns an array.
For this purpose Array.from is made - and this has a mapping function as well.
Instead of builing more than one array, you could take Array.from directly with an object with a length property and a mapping function.
const range = (min, max) => Array.from(
{ length: max - min + 1 },
(_, i) => i + min
);
console.log(...range(7, 9));

Trying to sum array of strings

Have an array of quotes (quotes), need to find the average length of one particular person's quotes (name).
I can find the values, but do not know how to get the sum of the returned values
let averageQuoteLength = (name) => {
i = crewMembers.indexOf(name)
a=(-1);
while (a <= quotes[i].length) {
a++
console.log(quotes[i][a].length)
}
}
assumes you have a 2 dimensional array more or less like:
var quotes = [
['quote1', 'quote2', 'quote3'],
['quote1', 'quote2', 'quote3'],
['quote1', 'quote2', 'quote3']
]
and some crewMembers array that has all names at the corresponding indices
try this out:
let averageQuoteLength = (name) => {
let i = crewMembers.indexOf(name);
return (quotes[i].reduce((acc, val) => acc += val.length, 0) / quotes[i].length);
};
the array reduce method sums all the length of all quotes in the array at quotes[i], and then I divide by the length of the array to get the average quote length. Reduce is a useful tool worth learning.

Modify an array without mutation

I am trying to solve a problem which states to remove(delete) the smallest number in an array without the order of the elements to the left of the smallest element getting changed . My code is -:
function removeSmallest(numbers){
var x = Math.min.apply(null,numbers);
var y = numbers.indexOf(x);
numbers.splice(y,1);
return numbers;
}
It is strictly given in the instructions not to mutate the original array/list. But I am getting an error stating that you have mutated original array/list .
How do I remove the error?
Listen Do not use SPLICE here. There is great known mistake rookies and expert do when they use splice and slice interchangeably without keeping the effects in mind.
SPLICE will mutate original array while SLICE will shallow copy the original array and return the portion of array upon given conditions.
Here Slice will create a new array
const slicedArray = numbers.slice()
const result = slicedArray.splice(y,1);
and You get the result without mutating original array.
first create a copy of the array using slice, then splice that
function removeSmallest(numbers){
var x = Math.min.apply(null,numbers);
var y = numbers.indexOf(x);
return numbers.slice().splice(y,1);
}
You can create a shallow copy of the array to avoid mutation.
function removeSmallest(numbers){
const newNumbers = [...numbers];
var x = Math.min.apply(null,newNumbers);
var y = newNumbers.indexOf(x);
newNumbers.splice(y,1);
return newNumbers;
}
array.slice() and [... array] will make a shallow copy of your array object.
"shallow" the word says itself.
in my opinion, for copying your array object the solution is:
var array_copy = copy(array);
// copy function
function copy(object) {
var output, value, key;
output = Array.isArray(object) ? [] : {};
for (key in object) {
value = object[key];
output[key] = (typeof value === "object") ? copy(value) : value;
}
return output;
}
Update
Alternative solution is:-
var arr_copy = JSON.parse(JSON.stringify(arr));
I'm not sure what the exact context of the problem is, but the goal might be to learn to write pure transformations of data, rather than to learn how to copy arrays. If this is the case, using splice after making a throwaway copy of the array might not cut it.
An approach that mutates neither the original array nor a copy of it might look like this: determine the index of the minimum element of an array, then return the concatenation of the two sublists to the right and left of that point:
const minIndex = arr =>
arr.reduce(
(p, c, i) => (p === undefined ? i : c < arr[p] ? i : p),
undefined
);
const removeMin = arr => {
const i = minIndex(arr);
return minIndex === undefined
? arr
: [...arr.slice(0, i), ...arr.slice(i + 1)];
};
console.log(removeMin([1, 5, 6, 0, 11]));
Let's focus on how to avoid mutating. (I hope when you say "remove an error" you don't mean "suppress the error message" or something like that)
There are many different methods on Array.prototype and most don't mutate the array but return a new Array as a result. say .map, .slice, .filter, .reduce
Telling the truth just a few mutate (like .splice)
So depending on what your additional requirements are you may find, say .filter useful
let newArray = oldArray.filter(el => el !== minimalElementValue);
or .map
let newArray = oldArray.map(el => el === minimalElementValue? undefined: el);
For sure, they are not equal but both don't mutate the original variable

Using Map-Reduce to Return A Modified Array of Strings

I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
For example,
Create a function that takes a number and returns an array of strings containing the number cut off at each digit.
420 should return ["4", "42", "420"]
My old Approach:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
var output = [];
for(var i = numArr.length-1; i>=0; i--) {
output.unshift(numArr.join('');
numArr.pop();
}
return output;
}
Attempt to use map-reduce combination:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element) {
var newElement = numArr.reduce(function(acc, val) {
return acc + val;
});
numArr.splice(element, 1);
return newElement;
});
}
You have used two loops, but apparently it can be done just with one.
function n(num) {
let res = (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
return res;
}
console.log(n(420));
console.log(n(13579));
One-liner.
const n = num => (""+num).split('').map((_,i) => (""+num).slice(0, i+1));
console.log(n(420));
console.log(n(13579));
As others noted, that this problem doesn't seem to be the best use case of the map and reduce functions.
map function provides the element, index and array information in the parameters. Making use of these you can iterate on the elements you need to apply the reduce function.
Statement var arrayToIterate = arr.slice(0,i+1); helps to achieve the above mentioned array to iterate.
Complete Code:
function createArrayOfTiers(num) {
var numArr = num.toString().split('');
return numArr.map(function(element, i, arr) {
var arrayToIterate = arr.slice(0,i+1);
var newElement = arrayToIterate.reduce(function(acc, val) {
return acc + val;
},"");
return newElement;
});
}
var result = createArrayOfTiers(420);
console.log(result);
I don't think these are good uses-cases of map or reduce, but here goes :
var numArr = [4,2,0];
var result = numArr.map(e => numArr.join('')) // now ["420", "420", "420"]
.map((arr, i) => arr.substring(0, i+1)) // now ["4", "42", "420"]
console.log(result);
We first replace every element of the array by the whole (unmodified) array joined into a string, then substring each of these strings based on their position in the outer array.
There's a reduction hidden in there, although not using reduce : join reduces [4, 2, 0] to "420".
I am learning map & reduce, but am having a hard time understanding how to utilize these methods to tackle problems.
Mapping associates to each value of the source array a new value provided by a mapping function : if you have an [x, y, z] array, a mapping function f(x)=x+1, the result of mapping the array with the function will be [x+1, y+1, z+1].
I believe reduction was meant to "reduce" an array to a primitive type, although I might be mistaken. If you have an [x, y, z] array and reduce it with the addition operation, the result will be x+y+z.

Categories

Resources