Cleaning up a function - javascript

So, guys, I have a strange question...
I have this code:
var valuesForLakewood = (function (m, k) {
return m[k] === undefined ? null : m[k];
})(this.lakewood, customerType);
var valuesForBridgewood = (function (m, k) {
return m[k] === undefined ? null : m[k];
})(this.bridgewood, customerType);
var valuesForRidgewood = (function (m, k) {
return m[k] === undefined ? null : m[k];
})(this.ridgewood, customerType);
lakewoodCost = weekdays * valuesForLakewood[0]
+ weekends * valuesForLakewood[1];
bridgewoodCost = weekdays * valuesForBridgewood[0]
+ weekends * valuesForBridgewood[1];
ridgewoodCost = weekdays * valuesForRidgewood[0]
+ weekends * valuesForRidgewood[1];
var hotel =
this.minCost(lakewoodCost, bridgewoodCost, ridgewoodCost);
I just need to clean it up a little bit. I repeat the same function over and over again and I would like your opinion on how I can clean this up a little bit, reducing the size of my code. Any thoughts? Thanks in advance!

Use a single function to extract the values from the object, two arrays instead of six standalone variables, destructuring to make the total cost operation more understandable, and then spread the resulting array into minCost:
const getVal = obj => obj[customerType] === undefined ? null : obj[customerType];
const values = [this.lakewood, this.bridgewood, this.ridgewood].map(getVal);
const costs = values.map(([weekdayCost, weekendCost]) => weekdays * weekdayCost + weekends * weekendCost);
const hotel = this.minCost(...costs);

Related

JavaScript convert array to counter

I want to know how to convert a one-dimensional array into a frequency dictionary with the elements of the array as keys, and their occurrences as values, in JavaScript.
For example, the following Python script creates a list of 1024 random numbers between 0 and 255 and counts the elements as described:
import random
from collections import Counter
sorted(Counter(random.randbytes(1024)).items(), key=lambda x: -x[1])
I can do the same in JavaScript, but much less concise:
var numbers = Array.from({length: 1024}, () => Math.floor(Math.random() * 256))
var counter = Object()
for (let number of numbers) {
if (counter.hasOwnProperty(number)) {counter[number] += 1}
else {counter[number] = 1}
}
Object.entries(counter).sort(([,a],[,b]) => b-a)
How do I make it more concise?
This surprised me: on my Mac, the Map version took 2.7s, and the Object version took 0.6s when testing with 100 million numbers.
(Your original version takes the same time as the Object version in the code below)
const numbers = Array.from({length:10**8},()=>Math.random()*256|0)
let timerStart = new Date()
{
let counter = new Map()
numbers.forEach(n=>counter.set(n,(counter.get(n)??0)+1))
counter = new Map([...counter].sort(([,a],[,b]) => b-a))
}
console.log(`Map version took ${new Date()-timerStart} ms`)
timerStart = new Date()
{
let counter = numbers.reduce((a,c)=>(a[c]=(a[c]??0)+1,a),{})
Object.entries(counter).sort(([,a],[,b]) => b-a)
}
console.log(`Object version took ${new Date()-timerStart} ms`)
Here's an ES6 one-liner to solve the problem, using Array.reduce and the fact that the value of a comma separated list of expressions is the value of the last one:
const numbers = Array.from({length: 1024}, () => Math.floor(Math.random() * 256))
const counter = numbers.reduce((acc, num) => (acc[num] = (acc[num] || 0) + 1, acc), {})
const sorted = Object.entries(counter).sort(([,a],[,b]) => b-a)
console.log(sorted)
You could try using a Map:
var numbers = Array.from({length: 1024}, () => Math.floor(Math.random() * 256))
const map = numbers.reduce((accum, d) => accum.set(d, (accum.get(d) || 0) + 1), new Map());
A quick "improvement" would be to use the ternary statement to make the code more concise.
var numbers = Array.from({length: 1024}, () => Math.floor(Math.random() * 256))
var counter = Object()
for (let number of numbers) {
//If counter has prop number, increment it by one otherwise set to 1
counter.hasOwnProperty(number) ? counter[number]++ : counter[number] = 1;
//Alternatively
//counter[number] = counter.hasOwnProperty(number) ? counter[number] + 1 : 1
}
Object.entries(counter).sort(([,a],[,b]) => b-a)

Finding specific string is odd or even from an array JavaScript

I am trying to find an odd string from an given array.
Code :
const friendArray = ["agdum", "bagdum", "chagdum", "lagdum", "jagdum", "magdum"];
function oddFriend(friendArray) {
for (let i = 0; i < friendArray.length; i++) {
if (friendArray[i].length % 2 != 0) {
return friendArray[i];
}
}
}
const myFriend = oddFriend(friendArray);
console.log(myFriend);
You can apply this:
const friendArray = ["agdum","bagdum","chagdum","lagdum","jagdum","magdum",];
function oddFriend(friendArray) {
if (friendArray.length % 2 != 0) {
return friendArray;
}
}
const myFriend = friendArray.filter(oddFriend);
console.log(myFriend);
.flatMap() and a ternary callback makes a simple and readable filter. It's not entirely clear if you wanted only odd or odd and even so there's both versions.
Odd length strings
const fArray = ["agdum", "bagdum", "chagdum", "lagdum", "jagdum", "magdum"];
let odd = fArray.flatMap(o => o.length % 2 === 1 ? [o] : []);
console.log(odd);
Odd & even length strings
const fArray = ["agdum", "bagdum", "chagdum", "lagdum", "jagdum", "magdum"];
let oe = [[], []];
fArray.forEach(o => o.length % 2 === 1 ? oe[0].push(o) : oe[1].push(o));
console.log(oe);
You can use Array.filter() method which will create a new array with all odd friends that pass the test implemented by the provided function.
// Input array
const friendArray = ["agdum", "bagdum", "chagdum", "lagdum", "jagdum", "magdum"];
// Filtered result
console.log(friendArray.filter((friend) => friend.length % 2 !== 0));

add leading zero to simple timestamp addition JavaScript function

I am trying to write a javascript function to sum up two timestamp strings, like 00:04:02 and 00:05:43
I have this function which works, but returns a value like: 0:9:45, I'm trying to improve it so there is a leading zero for the minutes section so it looks more like: 0:09:45 but im having trouble doing so and was wondering if anyone could help me:
function sum(date1, date2){
date1 = date1.split(":");
date2 = date2.split(":");
const result = [];
date1.reduceRight((carry,num, index) => {
const max = [24,60,60][index];
const add = +date2[index];
result.unshift( (+num+add+carry) % max );
return Math.floor( (+num + add + carry) / max );
},0);
return result.join(":");
}
console.log(sum('00:05:43', '00:04:02'))
Pad each digit?
return result.map(r => String(r).padStart(2, "0")).join(":");
If you use the built-in Date methods, you don't need to parse times or do math yourself.
'use strict';
function sumTimestamps(a, b) {
const [A, B] = [a, b].map((x) => new Date('1970-01-01T' + x + '.000Z'));
return new Date(A.getTime() + B.getTime()).toUTCString().split(' ')[4];
}
const sum = sumTimestamps('00:04:02', '00:05:43');
console.log(sum);
// => 00:09:45

How to distinguish time from am and pm in the value of a variable?

I have the following values.
var list = "09:05, 10:05, 12:30, 16:30 , ... , ..."
The type of values ​​in the list is a regular string, not an object.
Based on this value, I want to divide from 0 to 12 am and from 13 to 23 pm.
Therefore, the result I want is as follows.(If you check the log value)
var am = am 09:05 , am 10:05
var pm = pm 12:30 , pm 16:30
It may be a simple question, but it is a very difficult problem for me as a script novice.
Please help me.
Create a sort function first
var sort = ( a, b ) => convertToMin( a ) - convertToMin( b );
var convertToMin = ( a ) => ( items = a.split( ":" ).map( Number ), items[ 0 ] * 60 + items[ 1 ] );
Now use reduce to segregate the array
var output = list.reduce( (a,b) => //using reduce to iterate, a is the accumulator and b is item in array for current iteration
( convertToMin(b) > 12*60 ? a.pm.push( b ) : a.am.push( b ), a ) ,
{ am :[], pm : [] }) ; //accumulator is initialized to { am :[], pm : [] }
output.am.sort( sort );
output.pm.sort( sort );
Demo
var list = ["09:05", "10:05", "12:30", "16:30"];
var sort = (a, b) => convertToMin(a) - convertToMin(b);
var convertToMin = (a) => (items = a.split(":").map(Number), items[0] * 60 + items[1]);
var output = list.reduce((a, b) =>
(convertToMin(b) > 12 * 60 ? a.pm.push(b) : a.am.push(b), a), {
am: [],
pm: []
});
output.am.sort(sort);
output.pm.sort(sort);
console.log(output);
Here's what I'd do to solve this problem.
Separate the values in the string into an array. I'd Google javascript split string into array. Nothing wrong with Googling stuff; even seasoned devs have to do it all the time! At least I do. :)
Then create a for loop that goes through each element of the array. A good search for how to do that is javascript for loop array.
Then for each element, split the string again (this time by the :).
Then convert the first part into a number (javascript convert string
to integer) and see whether it is bigger or smaller than 12.
You could adjusted value with am/pm time and sort it to the wanted array.
function format(v) { return ('0' + v).slice(-2); }
function getM(t) {
var v = t.split(':');
return (v[0] < 12 ? 'am' : 'pm') + ' ' + [v[0] % 12 || 12, v[1]].map(format).join(':');
}
var list = '09:05, 10:05, 12:30, 16:30',
am = [],
pm = []
result = { am: am, pm: pm };
list
.split(', ')
.map(getM)
.forEach(function (s) {
result[s.slice(0, 2)].push(s);
});
console.log(am);
console.log(pm);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Split the items using the appropriate separator, process them with a cycle then join them with the appropriate separator
var items = list.split(", ");
var ams = [];
var pms = [];
for (var index = 0; index < list.length; index++) {
var isPM = ((list[index].substring(0, 2) >= 12);
var currentArray = window[isPM ? "pms" : "ams"];
var found = false;
var val = (isPM ? "pm" : "am") + " " + items[index];
for (var innerIndex = 0; (!found) && (innerIndex < currentArray.length); innerIndex++) {
if (currentArray[innerIndex] > val) {
found = true;
currentArray.splice(innerIndex, 0, val);
}
}
if (!found) currentArray.push(val);
}
var am = ams.join(" , ");
var pm = pms.join(" , ");
Try with .split method like this,
Updated without jQuery
var list = "09:05, 10:05, 12:30, 16:30";
var options = list.split(',').map(time => {
h = time.split(':')[0];
return parseInt(h) >= 12 ? 'pm ' + time : 'am ' + time;
})
console.log(options);

How to best sort an array of strings with comma delimited numbers _and_ symbols in JavaScript

What's the best approach to sort() an array of strings containing comma delimited numbers and symbols in JavaScript?
My array looks like this:
var uniqueValues = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"]
and I need it to look like this:
[">1,000,000", ">750,000", ">500,000", ">250,000", ">100,000", "<100,000"]
Below you can see what I've tried thus far. #1 to get around the alphanumeric issue (using a combo of sort & localeCompare) and #2 to attempt to weigh ">" higher than "<" for example (thanks to this SO answer, but I can't figure out how to both weigh the symbol, as well as correctly sort the comma-delimited numbers. Here are the approaches I've tried without success:
1. uniqueValues.sort((a,b) => a.localeCompare(b));
2. var sortOrder = {
'>': 1,
'<': 2
}
uniqueValues.sort(function (a, b) {
var oa = sortOrder[a[0]] || 2;
var ob = sortOrder[b[0]] || 2;
if (oa > ob)
return 1;
else if (oa < ob)
return -1;
else
return 0;
});
A slightly different approach is to use a parse method since you don't exactly want to sort according to string values. You rather want to sort according to the order of magnitude they represent.
Let assume that each expression should be interpreted as a bigger number ('>') or smaller number ('<'). That can be translated to +1 / -1. Then, replace all dots and commas in the remaining part for it to be an integer and sum both parts.
The solution can be as simple as a sort according to that parse method.
var uniqueValues = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"];
function parseExpr(exp) {
return (exp[0] == '<' ? -1 : 1) + parseInt(exp.replace(/[,.]/g, '').substr(1));
}
uniqueValues.sort((a,b) => parseExpr(b) - parseExpr(a));
console.log(uniqueValues);
Add 1 to the number if it includes >, then sort:
var uniqueValues = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"]
var s2i = (s) => {
let n = parseInt(s.replace(/[^\d]/g, ''))
return n + (s.includes('>') ? 1 : 0)
};
uniqueValues.sort((a,b) => Math.sign(s2i(a) - s2i(b)));
console.log(uniqueValues)
Not the most elegant or robust solution, but very simple:
uniqueValues.sort((a, b) => {
const aStr = a.substring(1)
const bStr = b.substring(1)
if (aStr == bStr) {
return a < b ? 1 : -1
}
const aInt = parseInt(bStr.split(',').join(''))
const bInt = parseInt(aStr.split(',').join(''))
return aInt - bInt
})
Convert your Strings into Numbers, and use an epsilon estimation instead of < and > for comparing.
Here's the code:
var a = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"];
var epsilon = 0.000001; // Choose whatever precision you require.
function toNumber(x) {
var signedEpsilon = (x[0] === "<" ? -epsilon : epsilon); // signed epsilon.
return parseFloat(x.slice(1).replace(/,/g, "")) + signedEpsilon;
}
a.sort((x, y) => toNumber(x) < toNumber(y));
Create a map of strings to numeric values using Array#reduce then sort using the numeric values from the map:
var uniqueValues = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"];
var valuesMap = uniqueValues.reduce(function(map, v) {
// convert to number by removing all non numeric characters, parsing, and convert the sign to a number
map[v] = {
value: +v.replace(/[^\d]/g, ''),
sign: v[0] === '>' ? 1 : -1
};
return map;
}, Object.create(null));
// sort using the corresponding values from the map - if the values are equal compare the signs
uniqueValues.sort(function(a, b) {
var am = valuesMap[a];
var bm = valuesMap[b];
return bm.value - am.value || bm.sign - am.sign;
});
console.log(uniqueValues);
And the ES6 version using a Map instead of a plain object:
var uniqueValues = ["<100,000", ">100,000", ">250,000", ">500,000", ">1,000,000", ">750,000"];
// convert to number by removing all non numeric characters, parsing, and convert the sign to a number
const valuesMap = uniqueValues.reduce((map, v) =>
map.set(v, {
value: +v.replace(/[^\d]/g, ''),
sign: v[0] === '>' ? 1 : -1
}), new Map());
// sort using the corresponding values from the map - if the values are equal compare the signs
uniqueValues.sort((a, b) => {
const am = valuesMap.get(a);
const bm = valuesMap.get(b);
return bm.value - am.value || bm.sign - am.sign
})
console.log(uniqueValues);

Categories