Javascript reduce with condition - javascript

How can I use reduce in order to return the sum only if the accumulator (a) and the currentValue (b) is both an Integer?
My guestGroups Array:
"guestGroups":[
{
"bookingNumber":"21.05.201827.05.20181208",
"arrivalDate":"2018-05-19T22:00:00.000Z",
"departureDate":"2018-05-25T22:00:00.000Z",
"customerId":1,
"fields":[
{
"value":"2",
"display_name":"Personen Anzahl",
"servicio_tags":[
"person-number-info"
]
}
],
"number":"041"
}
]
getPersonNumberAdult() {
const personNumberField = this.fields.find(f => {
return f.servicio_tags && f.servicio_tags.includes('person-number-info');
});
return personNumberField ? Number(personNumberField.value.match(/\d+/)[0]) : '-';
}
isInt(value) {
let x = parseFloat(value);
return !isNaN(value) && (x | 0) === x;
}
guestInfo: t.guestGroups
.map(gg => gg.getPersonNumberAdult())
.reduce((function(a, b) {
if (a && b) {
{
if (this.isInt(a) && this.isInt(b)) {
return a + b
} else {
return '-'
}
}
}
}), 0)
This gives me undefined.
Thanks for your efforts!

Your getPersonNumberAdult should not return either a number or a string. It should return an integer or NaN if no number is available. The '-' should only be used in formatting the output in the end.
Once you got that, you can either filter out invalid values and sum the rest:
const sum = t.guestGroups
.map(gg => gg.getPersonNumberAdult())
.filter(v => isInt(v)) // or just !isNaN
.reduce((a, b) => a + b, 0);
or just sum them and then replace NaN by '-' if any value was invalid:
const sum = t.guestGroups
.map(gg => gg.getPersonNumberAdult())
.reduce((a, b) => a + b, 0);
const output = isInt(sum) ? String(sum) : '-';
depending on which behaviour you want.

Related

Assign mutiple ranges (non consecutive) to a single Constants (apps script)

I have this (below) as part of my apps script and I am wondering if there is a way to simplify this:
const fieldRange = ["C9","C10","C11","C12","C15","C16","C17","C18","C19","C20","C21","C22","C23","C24","C25","C26","C27","C28","C29","C30","C33","C34","C35","C36","C37","C38","C41","C42","C45","C46","C47","C48","C49","C50"]
maybe something like ['(C9:C12)','(C15:C30)', etc.
In your situation, how about the following sample script?
Sample script:
// This value is from your question.
const fieldRange = ["C9", "C10", "C11", "C12", "C15", "C16", "C17", "C18", "C19", "C20", "C21", "C22", "C23", "C24", "C25", "C26", "C27", "C28", "C29", "C30", "C33", "C34", "C35", "C36", "C37", "C38", "C41", "C42", "C45", "C46", "C47", "C48", "C49", "C50"];
// Ref: https://tanaikech.github.io/2021/10/08/compiling-continuous-numbers-using-google-apps-script/
const compilingNumbers = (ar) => {
const { values } = [...new Set(ar.sort((a, b) => a - b))].reduce((o, e, i, a) => {
if (o.temp.length == 0 || (o.temp.length > 0 && e == o.temp[o.temp.length - 1] + 1)) {
o.temp.push(e);
} else {
if (o.temp.length > 0) {
o.values.push({ start: o.temp[0], end: o.temp[o.temp.length - 1] });
}
o.temp = [e];
}
if (i == a.length - 1) {
o.values.push(o.temp.length > 1 ? { start: o.temp[0], end: o.temp[o.temp.length - 1] } : { start: e, end: e });
}
return o;
}, { temp: [], values: [] });
return values;
};
const r1 = fieldRange.reduce((o, e) => {
const k = e.replace(/[0-9]+/, "");
const v = Number(e.toUpperCase().replace(/[A-Z]+/, ""));
o[k] = o[k] ? [...o[k], v] : [v];
return o;
}, {});
const res = Object.entries(r1).flatMap(([k, v]) => compilingNumbers(v).map(({ start, end }) => `${k}${start}:${k}${end}`));
console.log(res); // ["C9:C12","C15:C30","C33:C38","C41:C42","C45:C50"]
When this script is run, ["C9:C12","C15:C30","C33:C38","C41:C42","C45:C50"] is obtained.
If you want to retrieve the value like maybe something like ['(C9:C12)','(C15:C30)', etc., please modify ${k}${start}:${k}${end} to (${k}${start}:${k}${end}).
Note:
From your showing sample value, this script supposes that all values in your expected array including A1Notation are only one cell. Please be careful about this.
References:
reduce()
map()

How to get the sum of the lengths of all arrays?

I receive different strings as arguments, there may be several of them.
I check in the if block if there are more than 1 argument, then I need to return the sum of the lengths of the lines that were passed in the function arguments. How can i do this?
const strSum = (...args) => {
let sum = 0;
if (args.length > 1) {
args.forEach((item) => {
});
}
return sum;
};
console.log(strSum('hello', 'hi', 'my name', 'is')); //16
You can add item.length to the sum. item.length will be equals to the length of the string.
const strSum = (...args) => {
let sum = 0;
if (args.length > 1) {
args.forEach((item) => {
sum += item.length
});
}
return sum;
};
console.log(strSum('hello', 'hi', 'my name', 'is')); //16
console.log(strSum()); //0
You can use array.reduce
const strSum1 = (...args) => {
if (args.length <= 1) return 0;
return args.reduce((sum, item) => { return sum + item.length}, 0)
};

Need to generate a new string in javascript

I'm trying to solve a question to create a new compressed string. Each character has a count next to it and I was supposed to come with up a new string in alphabetical order followed by the total count of that character within the string.
For eg:
Input: "a10b1a5c1"
Output: "a15b1c1"
Input: "b10a1b5c1"
Output: "a1b15c1"
How I approached this question?
Have an object with key as char and value as count
Convert the object to string
function stringCompression(s) {
const object={}
for(var i=0; i<s.length-1; i++) {
if(s[i].match(/[0-9]/)!==null && s[i+1].match(/[a-zA-Z]/) === null) {
if(object.hasOwnProperty("s[i]")){
// Add previous and current value
} else {
object[s[i-1]]=parseInt(s[i]+s[i+1])
}
}
}
return object
}
// output should be a15b1c1
const output= stringCompression("a10b1a5c1");
const final = Object.keys(output).map(key => `${key}${output[key]}`).join("");
console.log(final)
Question:
Can't think of a way to sort alphabetically
The function I wrote doesn't handle if the character has a single digit count
Is there any other way I can optimise the solution?
You could reduce() everything to a single "counter" object, and then use Object.entries() to convert that object to an array and perform sorting, flattening and joining on that array:
const compress = (s) => Object.entries(
[...s.matchAll(/([A-Za-z])(\d+)/g)].reduce((a, [_, char, number]) => ({
...a,
[char]: (a[char] || 0) + +number
}), {})
).sort(([a], [b]) => a.localeCompare(b)).flat().join('');
console.log(compress('b10a1b5c1'));
const STRING = 'a10c8b3a4'
const isDigit = (charCode) => {
return charCode >= 48 && charCode <=57
}
const isAlphabet = (charCode) => {
return charCode >= 97 && charCode <= 122
}
const getSortedObject = (obj) => {
return Object.keys(obj).sort().reduce(
(object, key) => {
object[key] = obj[key];
return object;
}, {}
);
}
const getCompressedString = (string) => {
let obj = {}
let alphabet = ''
let digit = ''
let i = 0
while(i<string.length) {
const char = string[i]
const charCode = string.charCodeAt(i)
if(isAlphabet(charCode)) {
alphabet = char
if(!obj[alphabet]) {
obj[alphabet] = '0'
}
i++
} else if(isDigit(charCode)) {
digit = '0'
while(isDigit(string.charCodeAt(i)) && i<string.length) {
digit += string[i]
i++
}
obj[alphabet] = parseInt(obj[alphabet]) + parseInt(digit)
}
}
let compressedString = ''
Object.keys(getSortedObject(obj)).forEach(key => {compressedString += key + obj[key]})
return compressedString
}
console.log(getCompressedString(STRING))

Sorting table column by id and field not working

I use the following method in order to sort data (array) in a reusable Material datatable:
sortData(sortParameters: Sort) {
const keyName = sortParameters.active;
if (sortParameters.direction === 'asc') {
this.tableData = this.tableData.sort((a, b) => a[keyName].localeCompare(b[keyName]));
}
else if (sortParameters.direction === 'desc') {
this.tableData = this.tableData.sort((a, b) => b[keyName].localeCompare(a[keyName]));
}
else {
this.getStudents();
}
}
However, althought it works for name field, it does not work for id field. Another issue regarding to this method is that I want to ignore whitespaces of the name and want to use trim() method for the sorted values. It is working, but I think I have to find another solution that works for id columns also. Is there any workaround for that?
Easiest way would be to use d3's ascending() and descending().
import {ascending, descending} from 'd3-array';
sortData(sortParameters: Sort) {
const keyName = sortParameters.active;
if (sortParameters.direction === 'asc') {
this.tableData = this.tableData.sort((a, b) =>
a_prop = a[keyName];
b_prop = b[keyName];
try {
a_prop = a_prop.trim();
b_prop = b_prop.trim();
} catch (error) {
//do nothing, must not have been a string
}
ascending(a_prop, b_prop);
}
else if (sortParameters.direction === 'desc') {
descending(a_prop,b_prop);
}
else {
this.getStudents();
}
}
Alternative to importing D3.js, paste the code for the functions directly in your code since they are short. Taken from
https://github.com/d3/d3-array/blob/master/src/ascending.js
ascending (a, b) {
return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
}
descending (a, b) {
return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
}
localeCompare is a function of Strings, but if your other columns, like id, are not strings then you can't use it to sort them. You need separate sort functions for each type then. Here is one way to do this:
const defaultSort = (a, b) => a < b;
const sortFunctions = {
id: (a, b) => a < b,
name: (a, b) => a.localeCompare(b),
// ....
};
function sortData(sortParameters: Sort) {
const keyName = sortParameters.active;
const sortFunction = sortFunctions[keyName] || defaultSort;
if (sortParameters.direction === 'asc') {
this.tableData = this.tableData.sort((a, b) => sortFunction(a, b));
}
else if (sortParameters.direction === 'desc') {
this.tableData = this.tableData.sort((a, b) => sortFunction(b, a));
} else {
this.getStudents();
}
}

JavaScript - compare multiple arrays to one

I would like to output which of 'buns' and 'duns' has the most elements in common with 'me'. How should I do this?
var buns = ['bap', 'bun', 'bop'];
var duns = ['dap', 'dun', 'dop'];
var me = ['dap', 'bun', 'bop'];
reduce over the array to be tested and check if each element is in me.
function common(arr1, arr2) {
return arr2.reduce(function (p, c) {
if (arr1.indexOf(c) > -1) p++;
return p;
}, 0);
}
common(me, buns); // 2
common(me, duns); // 1
DEMO
Index important
function hamd(a, b) {
return a.reduce((sum, item, i) => {
if (item !== b[i]) return sum + 1;
return sum;
}, 0);
}
Index not important, duplicates not important
function diff(a, b) {
return a.reduce((sum, item, i) => {
if (b.indexOf(item) === -1) return sum + 1;
return sum;
}, 0);
}
Index not important, duplicates important
function diffU(a, b) {
b = b.slice();
return a.reduce((sum, item, i) => {
let i = b.indexOf(item);
if (i === -1) return sum + 1;
b.splice(i, 1);
return sum;
}, 0);
}
The one with the lowest score has the most similarity, e.g.
diff(me, buns) // 1
diff(me, duns) // 2
// `buns` is more like `me` than `duns`
Please note that these are not commutative operations when the length of the Array is not the same; diff(a, b) may be different to diff(b, a) when a.length !== b.length
If you want to compare the results you need the common array on the same side of all tests

Categories

Resources