How to add missing values to two associated arrays? (JavaScript) - javascript

I have two arrays:
category.data.xAxis // containing: ["0006", "0007", "0009", "0011", "0301"]
category.data.yAxis // containing: [6.31412, 42.4245, 533.2234, 2345.5413, 3215.24]
How do I take a max length, say DATAPOINT_LENGTH_STANDARD = 540 and fill in every missing number-based string on the xAxis array? so that:
The resulting xAxis array will read from "0000" to "0540" (or whatever the standard length is)
The associated yAxis indexes will remain connected to the original xAxis datapoints (i.e. "0006" to 6.31412)
Every newly created xAxis datapoint has an associated yAxis value of 0 (so the newly created "0000" xAxis entry will contain 0 in the yAxis at index 0 for both)
The xAxis value strings can be assumed to already be sorted from lowest to highest.
EDIT
The clarity of my question was an effort to help the community with finding a more straight-forward answer, but I suppose the exactness gave the appearance of a homework question. In response to the comments, my original attempt, which does not properly manipulate the x-axis and maintain proper indexing:
let tempArray = categoryObject.data.xAxis;
let min = Math.min.apply(null, tempArray);
let max = Math.max.apply(null, tempArray);
while (min <= max) {
if (tempArray.indexOf(min.toString()) === -1) {
tempArray.push(min.toString());
categoryObject.data.yAxis.push(0);
}
min++;
}
console.log(tempArray);
console.log(categoryObject.data.yAxis);

let xAxis = ["0006", "0007", "0009", "0011", "0301"]
let yAxis = [6.31412, 42.4245, 533.2234, 2345.5413, 3215.24]
const DATAPOINT_LENGTH_STANDARD = 540
// This assumes at least one data point
let paddedLength = xAxis[0].length
// Creates a new array whose first index is 0, last index is
// DATAPOINT_LENGTH_STANDARD, filled with 0s.
let yAxis_new = new Array(DATAPOINT_LENGTH_STANDARD + 1).fill(0)
// Copy each known data point into the new array at the given index.
// The + before x parses the zero-padded string into an actual number.
xAxis.forEach((x, i) => yAxis_new[+x] = yAxis[i])
// Replace the given array with the new array.
yAxis = yAxis_new
// Store the padded version of the index at each index.
for (let i = 0; i <= DATAPOINT_LENGTH_STANDARD; ++i) {
xAxis[i] = ('' + i).padStart(paddedLength, '0')
}
console.log(xAxis)
console.log(yAxis)

Related

How to find a tuple in an array of unique intergers that add to a target number in javascript

I am trying to solve the first question of Advent of Code 2020 in Javascript. I am tasked with finding three numbers that add to a target number. In this case, the target is 2020.
For example:
There should be three numbers: [1721, 979, 366, 299, 675, 1456] that add to 2020. In this case, those numbers are [366, 675, 979]
My approach: By Sorting the array the efficiency of the algorithm can be improved. This efficient approach uses the two-pointer technique. Traverse the array and fix the first element of the triplet. Now use the Two Pointers algorithm to find if there is a pair whose sum is equal to x – array[i]. Two pointers algorithm take linear time so it is better than a nested loop.
Algorithm: Sort the given array.
Loop over the array and fix the first element of the possible triplet, arr[i].
Then fix two pointers, one at i + 1 and the other at n – 1. And look at the sum,
If the sum is smaller than the required sum, increment the first pointer.
Else, If the sum is bigger, Decrease the end pointer to reduce the sum.
Else, if the sum of elements at two-pointer is equal to given sum then print the triplet and break.
Note: I am expanding this approach from a python thread and really like it, but while trying to create it in javascript, I am having some trouble.
Currently, this is what I have:
function threeSumBest(array, target) {
// sort elements (accending)
const sortedAsc = array.sort((a, b) => a - b)
for (let i = 0; i < sortedAsc.length; i++) {
let result = []
// index of the first element in the remaining elements
let firstElement = i + 1
// # index of the last element
let lastElement = sortedAsc.length - 1
// create a sum variable
let sum = (sortedAsc[i] + sortedAsc[firstElement] + sortedAsc[lastElement])
// console.log(sortedAsc[i], sortedAsc[firstElement], sortedAsc[lastElement], sum);
// console.log(sortedAsc[i], sortedAsc[firstElement], sortedAsc[lastElement]);
console.log(result);
if (sum > target) {
console.log('greater than');
lastElement--
}
if (sum < target) {
console.log('less than');
result[0] = sortedAsc[i++]
result[1] = sortedAsc[firstElement]
result[2] = sortedAsc[lastElement]
}
if (sum == target) {
console.log('equal');
}
}
}
console.log(threeSumBest([1721, 979, 366, 299, 675, 1456], 2020))
You're missing the inner loop to iterate on firstIndex to lastIndex.
the following code will work for you
for (let i = 0; i < sortedAsc.length; i++) {
let result = []
// index of the first element in the remaining elements
let firstElement = i + 1
// # index of the last element
let lastElement = sortedAsc.length - 1
while(firstElement<lastElement){
let sum = (sortedAsc[i] + sortedAsc[firstElement] + sortedAsc[lastElement])
if (sum > target) {
//console.log('greater than');
firstElement++;
}
if (sum < target) {
//console.log('less than');
lastElement--;
}
if (sum == target) {
result[0] = sortedAsc[i]
result[1] = sortedAsc[firstElement]
result[2] = sortedAsc[lastElement]
console.log('equal');
break;
}
}
}
console.log(threeSumBest([1721, 979, 366, 299, 675, 1456], 2020))

How to loop through an array of numbers to see which numbers are relevant to another number?

I'm trying to write a simple program in vanilla JavaScript for weightlifting. The user inputs a certain amount of weight and it returns the specific weight plates to put on each side of the barbell.
I then take that number into a function which subtracts 45 from it to account for the barbell weight then divides that number by 2 which is the amount of weight to put on each side of the bar.
const num = document.getElementById("weightAmount").value;
function getWeightAmount (num) {
const newNum = num - 45;
const halfNum = newNum / 2;
return getWeights(halfNum);
}
I have an array with each weight plate:
let plates = [44, 33, 22, 11, 5.5, 2.75];
I'm having trouble correctly looping through the array to get what I want. If I need, say, 60.5 lbs on each side, it should return 44, 11, 5.5. So I need to figure out which numbers in the plate array fit in the number returned from my first function.
I have an empty array called weights which I want to push the numbers from the plates array into that work with the weight which then is returned.
My question is how do I loop through the plates array to figure out which weights are needed?
A possible solution to this is iterating indefinitely until either
you have a solution
the problem becomes unsolvable given the set of weights
Every iteration step, you subtract the highest possible weight times the highest possible factor, store both in a suitable data structure (my implementation simply uses an Object) and continue.
const plates = [44, 33, 22, 11, 5.5, 2.75];
// We assume that plates is always sorted
const determineWeights = (totalWeight) => {
let factor = 0;
let weights = {};
while (totalWeight > 0) {
weight = plates.find(weight => Math.floor(totalWeight / weight) > 0);
// There is no weight we can subtract from the total weight to solve the problem
// Hence, the problem is unsolvable and we return null to indicate that no solution exists
if (!weight) { return null; }
// Determine the factor with which to multiply the weight before we subtract from the total weight an subtract the product
factor = Math.floor(totalWeight / weight);
totalWeight = totalWeight - factor * weight;
// Store weight and factor
weights[weight] = factor;
}
return weights;
}
console.log(determineWeights(104.5)); // { "11": 1, "44": 2, "5.5": 1 }
console.log(determineWeights(60.5)); // { "11": 1, "44": 1, "5.5": 1 }
console.log(determineWeights(5.0)); // null
The problem is essentially an instance of the Knapsack problem.
Note that we assume that plates is sorted. Otherwise, Array.find will not necessarily retrieve the maximum weight that can be subtracted from the total weight.
I have a simple solution below if value of the target weight will always be the sum of the available plates. Assuming the weights array are sorted in a descending order. I loop thought all available weights and will only proceed to the next weight if the total exceeds the total weight you require.
function getWeights(targeWeight) {
let plates = [44, 33, 22, 11, 5.5, 2.75];
let totalWeight = 0;
let neededPlates = [];
let i = 0;
while(i < plates.length){
var pweight = totalWeight + plates[i];
if (pweight > targeWeight) {
i++;
continue;
}
totalWeight += plates[i];
neededPlates.push(plates[i]);
}
return neededPlates;
}
console.log(getWeights(60.5)); // [44, 11, 5.5]
console.log(getWeights(104.5)); //[44, 44, 11, 5.5]
Here's a solution. In the event that the available plates don't add up to the target weight, it will return the combination of available plates that add up closest to the target. Adapted from this answer.
function createSubsets(numbers, target) {
// filter out all items larger than target
numbers = numbers.filter(function (value) {
return value <= target;
});
// sort from largest to smallest
numbers.sort(function (a, b) {
return b - a;
});
var i;
var sum = 0;
var addedIndices = [];
// go from the largest to the smallest number and
// add as many of them as long as the sum isn't above target
for (i = 0; i < numbers.length; i++) {
if (sum + numbers[i] <= target) {
sum += numbers[i];
addedIndices.push(i);
}
}
return addedIndices.map(n => numbers[n]);
}

Generate list of items with 0 prefix using padStart

I implemented the way to generate a list of items with iterable counts with prefix 0. What is the best way to generate such kind of list?
Current behaviour:
const generateList = (length, n, i) => {
let b = n+i
return b.toString().padStart(length.toString().length + n.toString.length, 0)
}
Array(10).fill(null).map((x, i) => generateList(10,2, i))
Output result:
["002", "003", "004", "005", "006", "007", "008", "009", "010", "011"]
Do u have any idea to make it another way?
You could determine the number of characters needed at the start and used the predetermined value to format the output for the array.
function createList(startValue, endValue) {
let
// The minimum output length, for a single digit number, is 2 chars.
outputLength = 2,
testValue = 10,
// Create an empty array which has as many items as numbers we need to
// generate for the output. Add 1 to the end value as this is to be
// inclusive of the range to create. If the +1 is not done the resulting
// array is 1 item too small.
emptyArray = Array(endValue - startValue + 1);
// As long as test value is less than the end value, keep increasing the
// output size by 1 and continue to the next multiple of 10.
while (testValue <= endValue) {
outputLength++;
testValue = testValue * 10;
}
// Create a new array, with the same length as the empty array created
// earlier. For each position place a padded number into the output array.
return Array.from(emptyArray, (currentValue, index) => {
// Pad the current value to the determined max length.
return (startValue + index).toString().padStart(outputLength, '0');
});
}
function createListWithLength(length, startValue = 0) {
return createList(startValue, startValue + length);
}
console.log(createList(2,10));
console.log(createListWithLength(30));
console.log(createListWithLength(10, 995));
Have a look at generators:
function* range(from, to) {
for (var i=from; i<to; i++)
yield i;
}
function* paddedRange(from, to) {
const length = (to-1).toString(10) + 1 /* at least one pad */;
for (const i of range(from, to))
yield i.padStart(length, '0');
}
console.log(Array.from(paddedRange(2, 12)));
You can also inline the loop from range into paddedRange, or you can make it return an array directly:
function paddedRange(from, to) {
const length = (to-1).toString(10) + 1 /* at least one pad */;
return Array.from(range(from, to), i => i.padStart(length, '0'));
}
console.log(paddedRange(2, 12));
The main simplification is that you should compute the padding length only once and give it a denotative name, instead of computing it for every number again. Also ranges are usually given by their lower and upper end instead of their begin and a length, but you can easily switch back if you need the latter for some reason.
Not sure, but maybe something like this
const generateList = length => Array(length).fill('0').map((item, index) => item + index);
console.log(generateList(20));

Find the lowest combination of predefined numbers, which sum is higher than X

lets say I have an array with different item-prices.
var myItemsEuro = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94]
I would like to have function like this:
function getTradeItems(0.89) { //The price of the item I want to buy
//Calculate, which of my items should be used to buy the item for 0.89€
return [0, 3, 6] //The position of my items in the array, which added together equal 0.90€
}
To clear things up:
I have a box of items with pricetags on them (myItemsEuro). I want to buy an item, using my items as a payment. The other party will accept my trade, if I overpay with atleast one cent.
The function should work, so i can pass the other guy's price to it (0.89 for example) and it returns, which items I will have to give away. The combination of these items must be above 0.89 cents (atleast 0.9), but should be as low as possible!
I am quite new to JS, and I was thinking about calculating every single combination of my items and then use the one that has the lowest difference to the buyprice. This seems really complicated to me and I don't even know how I would make it calculate every single combination and also save which items were used for the calculation.
Is there any way to achieve this a bit more efficient? I don't really expect any perfectly working code here, a little bit of help to get into the right direction would also be nice.
Any help is appreciated! :)
Edit:
Sorry for missing my own attempt. It's just that I have no idea how I should solve this at all. And no - not homework - this is supposed to be part of a chromeextension I am working on!
var myItemsEuro = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94]
function getTradeItems(marketPrice) {
var result = 0;
var positions = [];
for(i = 0; i < myItemsEuro.length; i++) {
result += myItemsEuro[i]; //add numbers from the array
positions.push(i); //save the used numbers position
if(result > marketPrice) { //if result is greater than marketPrice...
console.log(result)
console.log(positions)
return positions; //return positions in the array
}
}
}
getTradeItems(1.31);
Edit:
Sorting the array and then adding up numbers doesn't give a solution.
var x = 1.18;
//Sorted by numbers
var myItemsEuro = [0.05, 0.11, 0.13, 0.20, 0.35, 0.50, 0.60, 0.69, 0.75];
//Add together and stop when sum > x:
0.05 + 0.11 + 0.13 + 0.20 + 0.35 + 0.50 = 1.34
//Best solution would be adding [6] and [8] from the array
0.50 + 0.69 = 1.19
You could use a brute force approach and test all combinations of the items if they are greater or equal of the target.
The base consideration is to take a counter from zero up to 2values.length and check if the actual 2index is part of the counter with a bitwise AND &. If so, take the value from the index and put it into the parts array.
Then build the sum of the parts and check if sum is greater or equal of target and possibly smaller than sum in result array, then move the result to the result array. If sum is equal to result[0].sum, then push the actual parts and sum to the result.
This proposal works with unsorted values, but could be more efficient, if the values which are greater then the target value are not included in the array to work on.
Another draw back is the fact, that bitwise arithmetic works only with 32 bit, that means arrays with more items than 32 are not possible use.
var values = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94],
target = 0.90,
i,
l = 1 << values.length,
result = [],
parts,
sum;
for (i = 0; i < l; i++) {
parts = values.filter(function (_, j) {
return i & 1 << j;
});
sum = parts.reduce(function (a, b) { return a + b; }, 0);
if (sum >= target) {
if (!result.length || sum < result[0].sum) {
result = [{ parts: parts, sum: sum }];
continue;
}
if (sum === result[0].sum) {
result.push({ parts: parts, sum: sum });
}
}
}
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I would suggest some measures to take:
Fractional numbers can cause floating point precision issues, so it is better to first convert every value to an integer (i.e. value in cents)
Perform a recursive search, where at each recursion level you'll decide whether or not to take the item at the corresponding index.
Think of situations where you might have multiple solutions: maybe in those cases you want to give precedence to solutions that use fewer items. So you would need to keep track of the number of items selected.
The solution I propose here will backtrack as soon as it is clear there is no sense in continuing adding items. There are at least two situations where you can draw this conclusion (of stopping the recursive search):
When the sum of the values selected so far is already greater than the target value, then there is no sense in adding more items (via recursion)
If after adding an item at position i, it becomes clear that adding all of the remaining items (via recursion) leads to a sum that is lower than the target, then it makes no sense to not select the item at position i, and repeat the recursive search, as that certainly will not reach the target value either.
Here is the suggested code:
function getTradeItems(target, items) {
var best = -1e20, // should be maximised up to -1 if possible
bestTaken = [], // indices of the best selection
bestCount = 0, // number of selected items in best solution
// Multiply all amounts by 100 to avoid floating point inaccuracies:
cents = items.map( euro => Math.round(euro * 100) );
function recurse(target, i, count) {
if (target < 0) { // Found potential solution
// Backtrack if this is not better than the best solution
// found so far. If a tie, then minimise the number of selected items
if (target < best ||
target === best && count > bestCount) return false;
best = target;
bestTaken = [];
bestCount = count;
return true;
}
// Give up when there's not enough item value available
if (i >= cents.length) return null;
// Include the item at index i:
var res1 = recurse(target - cents[i], i+1, count+1);
// If we could not reach the target, don't lose time retrying
// without this item:
if (res1 === null) return null;
// Exclude the item at index i:
var res0 = recurse(target, i+1, count);
// If neither led to a solution...
if (!res0 && !res1) return false;
// If the best was to include the item, remember that item
if (!res0) bestTaken.push(i);
return true;
}
recurse(Math.round(target * 100), 0);
return bestTaken.reverse();
}
// Sample input
var myItemsEuro = [0.05, 0.11, 0.13, 0.20, 0.35, 0.50, 0.60, 0.69, 0.75];
var x = 1.18
// Get the best item selection
var result = getTradeItems(x, myItemsEuro);
// Output result
console.log('Selected: ', result.join()); // 5, 7
// Show corresponding sum: (1.19)
console.log('Sum: ', result.reduce( (sum, i) => sum + myItemsEuro[i], 0).toFixed(2));

Finding the nth item in a repeating list of fixed items

I have to determine the mathematical formula to calculate a particular repeating position in a series of numbers. The list of numbers repeats ad infinitum and I need to find the number every n numbers in this list. So I want to find the *n*th item in a list of repeating y numbers.
For example, if my list has 7 digits (y=7) and I need every 5th item (n=5), how do I find that item?
The list would be like this (which I've grouped in fives for ease of viewing):
12345 67123 45671 23456 71234 56712 34567
I need to find in the first grouping number 5, then in the second grouping number 3, then 1 from the third group, then 6, then 4, then 2, then 7.
This needs to work for any number for y and n. I usually use a modulus for finding *n*th items, but only when the list keeps increasing in number and not resetting.
I'm trying to do this in Javascript or JQuery as it's a browser based problem, but I'm not very mathematical so I'm struggling to solve it.
Thanks!
Edit: I'm looking for a mathematical solution to this ideally but I'll explain a little more about the problem, but it may just add confusion. I have a list of items in a carousel arrangement. In my example there are 7 unique items (it could be any number), but the list in real terms is actually five times that size (nothing to do with the groups of 5 above) with four sets of duplicates that I create.
To give the illusion of scrolling to infinity, the list position is reset on the 'last' page (there are two pages in this example as items 1-7 span across the 5 item wide viewport). Those groups above represent pages as there are 5 items per page in my example. The duplicates provide the padding necessary to fill in any blank spaces that may occur when moving to the next page of items (page 2 for instance starts with 6 and 7 but then would be empty if it weren't for the duplicated 1,2 and 3). When the page goes past the last page (so if we try to go to page 3) then I reposition them further back in the list to page one, but offset so it looks like they are still going forwards forever.
This is why I can't use an array index and why it would be useful to have a mathematical solution. I realise there are carousels out there that do similar tasks to what I'm trying to achieve, but I have to use the one I've got!
Just loop every 5 characters, like so:
var data = "12345671234567123456712345671234567";
var results = [];
for(var i = 4; i < data.length; i += 5){
results.push(data[i]);
}
//results = [5, 3, 1, 6, 4, 2, 7]
If you want to use a variable x = 5; then your for loop would look like this:
for(var i = x - 1; i < data.length; i += x){...
There is no need to know y
If your input sequence doesn't terminate, then outputting every nth item will eventually produce its own repeating sequence. The period (length) of this repetition will be the lowest common multiple of the period of the input sequence (y) and the step size used for outputting its items (x).
If you want to output only the first repetition, then something like this should do the trick (untested):
var sequence = "1234567";
var x = 5;
var y = sequence.length;
var count = lcm(x, y);
var offset = 4;
var output = [];
for (var i = 0; i < count; i += x)
{
j = (offset + i) % y;
output.push(sequence[j]);
}
You should be able to find an algorithm for computing the LCM of two integers fairly easily.
A purely mathematical definition? Err..
T(n) = T(n-1) + K For all n > 0.
T(1) = K // If user wants the first element in the series, you return the Kth element.
T(0) = 0 // If the user want's a non-existent element, they get 0.
Where K denotes the interval.
n denotes the desired term.
T() denotes the function that generates the list.
Lets assume we want every Kth element.
T(1) = T(0) + K = K
T(2) = T(1) + K = 2K
T(3) = T(2) + K = 3K
T(n) = nk. // This looks like a promising equation. Let's prove it:
So n is any n > 1. The next step in the equation is n+1, so we need to prove that
T(n + 1) = k(n + 1).
So let's have a go.
T(n+1) = T(N+1-1) + K.
T(n+1) = T(n) + K
Assume that T(n) = nk.
T(n+1) = nk + k
T(n+1) = k(n + 1).
And there is your proof, by induction, that T(n) = nk.
That is about as mathematical as you're gonna get on SO.
Nice simple recurrence relation that describes it quite well there.
After your edit I make another solution;)
var n = 5, y = 7;
for (var i = 1; i<=y; i++) {
var offset = ( i*y - (i-1)*n ) % y;
var result = 0;
if (offset === n) {
result = y;
} else {
result = (n - offset) > 0 ? n - offset : offset;
}
console.log(result);
}
[5, 3, 1, 6, 4, 2, 7] in output.
JSFIDDLE: http://jsfiddle.net/mcrLQ/4/
function get(x, A, B) {
var r = (x * A) % B;
return r ? r : B;
}
var A = 5;
var B = 7;
var C = [];
for (var x = 1; x <= B; ++x) {
C.push(get(x, A, B));
}
console.log(C);
Result: [5, 3, 1, 6, 4, 2, 7]
http://jsfiddle.net/xRFTD/
var data = "12345 67123 45671 23456 71234 56712 34567";
var x = 5;
var y = 7;
var results = [];
var i = x - 1; // enumeration in string starts from zero
while ( i <= data.length){
results.push(data[i]);
i = i + x + 1;// +1 for spaces ignoring
}

Categories