Using Reduce method - javascript

I just started learning about reduce method, and stuck in this problem...
N answer
123 6
987 24
An example would be 9 + 8 + 7 = 24 but the parameter is in number.
Here is what I have tried:
function solution (n){
let result = new Array (n)
let answer = result.reduce((a,b) => {
return a + b
})
}
Here is my code but I'm getting TypeError [Error]: Reduce of empty array with no initial value

Here's how you would use reduce to calculate the sum -
[...String(123)].map(Number).reduce((a,b) => a+b, 0)

This:
let result = new Array (n)
creates an empty array with a length of n.
In order to convert a number to an array of digits, you can use this little handy snippet:
const arr = n.toString().split('').map(Number)
We are getting n as a string, then splitting it into an array of characters, and then mapping each character to its corresponding digit.
After that you can use the reduce function:
let answer = arr.reduce((a,b) => {
return a + b
});

Related

Sort/order a string array by two conditions (name sequence and date)- JavaScript

Having some issues trying to sort/order this string array. There are thousands of files names that come back in the response, below is an example of 10.
array = [
'ORDERHEADER_010122.arc',
'ORDERITEM_010122.arc',
'ORDERDETAIL_010122.arc',
'ORDERDETAIL_010222.arc',
'ORDERDETAIL_010322.arc',
'ORDERHEADER_010222.arc',
'ORDERHEADER_010322.arc',
'ORDERHEADER_010422.arc',
'ORDERITEM_010222.arc',
'ORDERDETAIL_010422.arc'
];
A simple array.sort() takes care of half of the issue as it will alphabetize the strings and inherently sort the dates.
What am needing is a "sequence" order of sorts along with the date order. So prioSequence = ['ORDERHEADER', 'ORDERDETAIL', 'ORDERITEM']; would be the sequence I want to see.
Expected output as:
array = [
'ORDERHEADER_010122.arc',
'ORDERDETAIL_010122.arc',
'ORDERITEM_010122.arc',
'ORDERHEADER_010222.arc',
'ORDERDETAIL_010222.arc',
'ORDERITEM_010222.arc',
'ORDERHEADER_010322.arc',
'ORDERDETAIL_010322.arc',
'ORDERHEADER_010422.arc',
'ORDERDETAIL_010422.arc'
];
Any help/guidance would be greatly appreciated! Thanks!
Prefix the string with the parts that determine the sort, i.e. yymmdd and 2 letters from the "ORDER" string, as it turns out that when you pick the 7th and 8th letter of those words (EA, ET, TE), they will be sorted correctly. Then after having sorted the items, remove that prefix again.
Here is how that works out:
let array = [
'ORDERHEADER_010122.arc',
'ORDERITEM_010122.arc',
'ORDERDETAIL_010122.arc',
'ORDERDETAIL_010222.arc',
'ORDERDETAIL_010322.arc',
'ORDERHEADER_010222.arc',
'ORDERHEADER_010322.arc',
'ORDERHEADER_010422.arc',
'ORDERITEM_010222.arc',
'ORDERDETAIL_010422.arc'
];
let sorted = array.map(item =>
item.replace(/ORDER.(..).*?_(..)(..)(..).*/g, "$4$3$2$1") + item
).sort().map(s => s.slice(8));
console.log(sorted);
Extending it
If you have more prefix-words of which you want to control the order, then create an array of those in their expected order. The solution will then turn that array into a lookup map (giving a 4-character sequence number for a given word). The call to replace then needs a callback argument, that will make the lookup and prefixes that sequence. Here is the code for that:
let array = [
'ORDERHEADER_010122.arc',
'ORDERITEM_010122.arc',
'ORDERDETAIL_010122.arc',
'ORDERDETAIL_010222.arc',
'ORDERDETAIL_010322.arc',
'ORDERHEADER_010222.arc',
'ORDERHEADER_010322.arc',
'ORDERHEADER_010422.arc',
'ORDERITEM_010222.arc',
'ORDERDETAIL_010422.arc'
];
let priorities = [
'ORDERHEADER',
'ORDERDETAIL',
'ORDERITEM',
];
// Map the priority array to an object for faster look-up
let priMap = Object.fromEntries(priorities.map((word, i) =>
[word, ("000" + i).slice(-4)]
));
let sorted = array.map(item =>
item.replace(/(.*?)_(..)(..)(..).*/g, (all, word, dd, mm, yy) =>
yy + mm + dd + (priMap[word] ?? "----") + all
)
).sort().map(s => s.slice(10));
console.log(sorted);
You have to define a custom compare function for your sorting method call. And, that method should first compare dates, and then (if a date is the same) order prefixes according to your requirements
Here is my example
const order = new Map() // one can use plain Array + Array#indexOf later on
.set('ORDERHEADER', 0)
.set('ORDERDETAIL', 1)
.set('ORDERITEM', 2)
const compareFn = (a, b) => {
const [a1, a2] = a.split('_')
const [b1, b2] = b.split('_')
const r2 = a2.localeCompare(b2) // TODO: re-write to date comparison a2 vs b2
if (r2 !== 0) return r2
return order.get(a1) - order.get(b1) // or Array#indexOf as mentioned above
}
// usage
array.sort(compareFn)

Javascript - How to return the largest four numbers in an array?

I have an array of strings that are numbers.
I am attempting to get back the largest 4 numbers in my array.
const data = [
"1,203,291",
"2,301,291",
"643,092",
"1,391,290",
"32,309",
"3,391"
]
I am attempting to return 2,301,291, 1,391,290, 1,203,291, 643,092
I first start off by removing the commas in a string and converting it into a number.
let topArr = data.map(e => Number(e.replace(/(,\s*)+/g, '').trim()));
Then create another variable which equals the max set of numbers.
let topValues = Math.max(...topArr)
//bring back the commas that were removed to append values with commas
String(topValues).replace(/(.)(?=(\d{3})+$)/g, '$1,')
I've used Math.Max but that only returns the largest number which is 2,301,291 is there a way to alter Math.Max in order to receive the top 4?
Here is my code in full code:
const data = [
"1,203,291",
"2,301,291",
"643,092",
"1,391,290",
"32,309",
"3,391"
]
let topArr = data.map(e => Number(e.replace(/(,\s*)+/g, '').trim()));
let topValues = Math.max(...topArr)
//bring back the commas that were removed to append values with commas
String(topValues).replace(/(.)(?=(\d{3})+$)/g, '$1,')
const data = [
"1,203,291",
"2,301,291",
"643,092",
"1,391,290",
"32,309",
"3,391"
];
let result = data.map(el => Number(el.split(",")
.join("")))
.sort((a,b) => b - a)
.splice(0, 4)
console.log(result);
sort them in descending order then use slice to get first 4
and then again use map and inside callback use the code to add thousand separator
const data = [
"1,203,291",
"2,301,291",
"643,092",
"1,391,290",
"32,309",
"3,391"
]
let topArr = data.map(e => Number(e.replace(/(,\s*)+/g, '').trim()))
// order in descending order
.sort((a, b) => {
return b - a;
})
// get the first 4
.slice(0, 4)
// return a new array & inside callback to add comma separator
.map((item) => {
return String(item).replace(/(.)(?=(\d{3})+$)/g, '$1,')
});
console.log(topArr)
Why not sort them after conversion to numbers and retrieve the last four numbers?

how to convert string values to numbers in a mixed array

I have a mixed array of values all string values. I want to take the string values representing a number and convert them to ints. Here is my array:
const data = 'new york;10.99;2000';
I split it transforming it:
const transData = data.split(';');
transData = ["new york", "10.99", "2000"]
I would like to iterate over that and return two clean int's with a string value for all non number strings. I am a bit stumped. I know this is easy to some but I have tried forEach, for, map, and I still get a NaN value for the first array member. I can't seem to filter or skip it even with an "if" check using for instance:
for(let i=0; i < transData.length; i++)
if(transData[i] != NaN){
transData[i] = + transData[i];
}else{continue};
I know how this works transData[i] = + transData[i];
I just can't seem to automate this thing with iteration/filter/map/whatever..
Any help greatly appreciated. These are baby steps into big boy/girl thinking in javascript.
Here are some of the methods I have tried:
const data = 'new york;10.99;2000';
const transData = data.split(';');
// transData[1] = + transData[1];
// transData[2] = + transData[2];
console.log(transData);
const filteredTransData = transData.filter(data => data > 0);
filteredTransData.forEach((data, idx) => {
filteredTransData[idx] = + filteredTransData[idx];
You can simply use || operater with .map() to get the desired output. As NaN is a falsey value so it would return the string under consideration as it is if it can't be converted to a number by Number:
const data = 'new york;10.99;2000';
const result = data.split(";").map(s => Number(s) || s);
console.log(result);
As pointed by #Maheer Ali:
Above solution has a limitation. It won't convert String "0" to Number 0 as 0 is a falsey value.
So we may use some other solution posted or we may explicitly apply a check for zero (0) where this modified array is being used.
NaN === NaN will always return false. To check if element is NaN you need to use isNaN function.
const transData = ["new york", "10.99", "2000"];
for(let i = 0; i< transData.length; i++){
if(!isNaN(transData[i])){
transData[i] = +transData[i];
}
}
console.log(transData)
Whenever you need to get a new value for each element of array based on previous value its better to use map()
const transData = ["new york", "10.99", "2000"];
const res = transData.map(x => isNaN(x) ? x : +x);
console.log(res)
Try something like this using MAP function
data.split(';').map((x)=>{
x= !isNaN(x)? parseFloat(x):x;
return x;
})
Yes, Maheer Ali's method is good. I just modified all code)
const data = 'new york;10.99;2000;0;-1;-2.45';
let transData = Array
.from(data.split(';'), x => parseFloat(x))
.filter( value => !Number.isNaN(value) );
console.log(transData);
Array.from() - creates a number array from the string array.
Array.filter() - removes NaN from the number array

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.

Mapping and reducing new int array issues

var strings = [ '234-496-7459', '760-644-0201', '555-222-3333' ];
var ints = [];
console.log(strings);
strings.forEach(function(entry) {
var ints = entry.replace(/-/g, '');
console.log(ints);
});
var myResults = ints.map(function (el) {
return el.toString().split('').reduce(function (sum, b) {
return sum + +b;
}, 0);
});
console.log(myResults);
I have an array of strings that I want to take out the dashes then store the new arrays as ints. I am trying to reduce each array of ints to create a myResults that print out 53, 30, 33. I know there is something wrong with this code because the mapping and reduce doesn't want to work.
You need to push entry values in ints array after replace
var strings = [ '234-496-7459', '760-644-0201', '555-222-3333' ];
var ints = [];
strings.forEach(function(entry) {
ints.push(entry.replace(/-/g, ''));
});
var myResults = ints.map(function (el) {
return el.toString().split('').reduce(function (sum, b) {
return sum + +b;
}, 0);
});
alert(myResults);
You can combine your statements like this (in a working snipppet):
var strings = [ '234-496-7459', '760-644-0201', '555-222-3333' ];
var ints = strings.map(function(item) {
return item.replace(/-/g, '').split('').reduce(function(total, val) {
return total + +val;
}, 0);
});
document.write(JSON.stringify(ints));
Explanation:
You want to convert one array to another so use .map() not .forEach().
Then, right in the .map() callback, you can convert the string to total.
.replace(/-/g, '').split('') gets rid of the dashes and turns it into an array of letters.
.reduce() then runs on that array to sum up all the digits.
If you're looking for another way, you can use
Array#map, String#replace, String#split, Array#reduce with Arrow function syntax.
You can use RegEx /\d/g with String#match to get individual numbers as an array.
var ints = arr.map(el => el.match(/\d/g, '').reduce((sum, a) => sum + +a, 0));
\d matches a single digit. g is global flag, to get all the possible matches.
var arr = ['234-496-7459', '760-644-0201', '555-222-3333'];
var ints = arr.map(el => el.match(/\d/g, '').reduce((sum, a) => sum + +a, 0));
console.log(ints);
document.write(ints);
String#replace and String#split can also be used.
var ints = arr.map(el => el.replace(/-/g, '').split('').reduce((sum, a) => sum + +a, 0));
var arr = ['234-496-7459', '760-644-0201', '555-222-3333'];
var ints = arr.map(el => el.replace(/-/g, '').split('').reduce((sum, a) => sum + +a, 0));
console.log(ints);
document.write(ints);
Equivalent code in ES5
var ints = arr.map(function (el) {
return el.replace(/-/g, '').split('').reduce(function (sum, a) {
return sum + +a;
}, 0);
});
var arr = ['234-496-7459', '760-644-0201', '555-222-3333'];
var ints = arr.map(function(el) {
return el.replace(/-/g, '').split('').reduce(function(sum, a) {
return sum + +a;
}, 0);
});
console.log(ints);
Approach this in top-down fashion, step-by-step. For each step, we will write a little spec so we are perfectly clear on what we want to do. If we write the spec well, then the JS will follow easily.
We want to write a function sum_digits_of_array, for which the spec is:
sum_digits_of_array
Given an input array, return a new array, where each element is the sum of the digits of the corresponding element from the original array.
That is exactly the definition of map, so we can write:
function sum_digits_of_array(a) { return a.map(sum_digits); }
Now we just need to write sum_digits. We will also write a spec for that:
sum_digits
Given an input string, return the sum of the digits of the string.
That's easy enough:
function sum_digits(s) { return sum(digits(s)); }
Now for digits. Again we will write a little spec for ourselves:
digits
Given a input string, return array of individual digits, as numbers.
Instead of thinking of this as removing dashes and then splitting, we will use RegExp#match to just extract the digits:
function digits(s) { return s.match(/\d/g) . map(Number); }
Then
sum
Given an array of numbers, return the sum of all the numbers.
This is the definition of reduce, so
function sum(a) { return a.reduce(add); }
Then
add
Given two numbers, return the result of adding them
function add(a, b) { return a + b; }
That's it. So the entire solution, which is slightly more compact if we use ES6 arrow functions:
function sum_digits_of_array(a) {
const sum_digits = s => sum(digits(s));
const digits = s => s.match(/\d/g) . map(Number);
const sum = a => a . reduce(add);
const add = (a, b) => a + b;
return a . map(sum_digits);
}
Why code in this fashion? There are a number of good reasons.
You can read the code and tell what it's doing.
Related to that, it's easy to figure out where to modify the code if the problem changes.
We can easily test each little function to make sure it's working right.
The little functions we wrote might come in handy when solving a related problem, so we reduce the amount of work we have to do in the future.

Categories

Resources