I have a constant array like this:
const pie_values = [20,10,5,5,10];
The challenge is to transform the above array based on integer input.
(5) => [5, 15, 10, 5, 5, 10]
(21) => [20, 1, 9, 5, 5, 10]
(31) => [20, 10, 1, 4, 5, 10]
An input of 5 takes 5 from the first index of pie_values. But it still leaves 15.
An input of 21 can take 20 from index 0 and 1 from index 2, leaving 9
I think you can see how this is going. So (0) and (50) will return the original pie_values.
Now the challenge is to create a function that does this in a few lines of code, which is build on loops rather than on 5 if statements. In case pie_values is extended upon, the function should still work.
I have an approach working with if statements, however the latter is undoable. What would be a good approach to these kind of problems?
First I defined a helper function:
//Returns summation of pie value
// totalPieValues(1) = 20
// totalPieValues(2) = 30
// totalPieValues(3) = 35
// totalPieValues(4) = 40
// totalPieValues(5) = 50
function totalPieValues(max) {
let result = 0;
for (let i = 0; i < max; i++) {
result += PIE_VALUES[i];
}
return result;
}
Then I worked on a function getPieArray which utilizes the helper function. This is where I am stuck
function getPieArray(wp) {
for (let i = 0; i < PIE_VALUES.length; i++) {
if (wp == 0 || wp == totalPieValues(i)) {
return PIE_VALUES;
}
}
let result = [];
for (let i = 1; i <= PIE_VALUES.length; i++) {
if (wp > totalPieValues(PIE_VALUES.length - i)) {
result.push(PIE_VALUES[i]);
} else if () {
result.push(wp - totalPieValues(3));
} else {
result.push(PIE_VALUES[i] - (value - totalPieValues(3)));
}
}
return result;
}
The code that I have written and works is here:
//Returns array of exact values needed to show in pie chart
export function getPieValues(wp) {
//1 => [1, 19, 10, 5, 5, 10]
//24 => [20, 4, 1, 5, 5, 10]
//31 => [20, 10, 1, 5, 5, 5, 10]
let result;
if (wp == 0) {
result = PIE_VALUES;
} else if (wp < totalPieValues(1)) {
result = [wp - totalPieValues(0), PIE_VALUES[0] - wp, PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(1)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(2)) {
result = [PIE_VALUES[0], wp - totalPieValues(1), PIE_VALUES[1] - (wp - PIE_VALUES[0]), PIE_VALUES[2], PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(2)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(3)) {
result = [PIE_VALUES[0], PIE_VALUES[1], wp - totalPieValues(2), PIE_VALUES[2] - (wp - totalPieValues(2)), PIE_VALUES[3], PIE_VALUES[4]];
} else if (wp == totalPieValues(3)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(4)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], wp - totalPieValues(3), PIE_VALUES[3] - (wp - totalPieValues(3)), PIE_VALUES[4]];
} else if (wp == totalPieValues(4)) {
result = PIE_VALUES;
} else if (wp < totalPieValues(5)) {
result = [PIE_VALUES[0], PIE_VALUES[1], PIE_VALUES[2], PIE_VALUES[3], wp - totalPieValues(4), PIE_VALUES[4] - (wp - totalPieValues(4))];
} else if (wp == totalPieValues(5)) {
result = PIE_VALUES;
}
return result;
}
-
Can you describe in more detail what the function is supposed to do? I’m not seeing it.Ry-– Ry- ♦2018年02月07日 23:52:05 +00:00Commented Feb 7, 2018 at 23:52
-
@Ryan say you have a pie with predefined sizes. In this case 20, 10, 5, 5 and 10. Now I want to take 1/50th of the total pie, which reshapes the pie in 1, 19, 10, 5, 5 and 10.Fullhdpixel– Fullhdpixel2018年02月08日 00:02:01 +00:00Commented Feb 8, 2018 at 0:02
8 Answers 8
This is super overkill
You can just iterate through the array and "eat" the index value and continue
function pieArray(inputArray, value){
let copyOfValue = value;
return inputArray.reduce((sum, input, index) => { // <-- index here
copyOfValue -= input;
if(copyOfValue > 0){
sum.push(input);
}else{
sum.push(input+copyOfValue);
sum.push(Math.abs(copyOfValue));
copyOfValue = Number.MAX_VALUE; //Hacky solution, just change value to max value
}
return sum;
}, []);
}
Tests
pieArray([20,10,5,5,10], 5) => [5, 15, 10, 5, 5, 10]
pieArray([20,10,5,5,10], 21) => [20, 1, 9, 5, 5, 10]
pieArray([20,10,5,5,10], 31) => [20, 10, 1, 4, 5, 10]
4 Comments
return [0].concat(inputArray) while the other is to check if we never inputted the value, then you need to remove the hacky solutionNumber.MAX_VALUE, that would make max value fail, but do you really need it?This is my approach. We iterate over our array, keeping track of our current value - and substracting from it as we push out each element to the output array.
There is 3 cases:
- either our current count is>= input, so we just push and move on,
- current count is 0, so we just push everything left
- current count is < input, but more than 0 - in this case we split.
Here is the code:
function transform(input, array) {
const total = array.reduce((previous, current) => previous + current);
// It wasn't specified what should happen when the input > total, so we will just throw an error.
if (input > total) {
throw new Error('Input cannot be bigger than the sum of elements in the array.');
}
let current = input;
let result = [];
for (let i = 0; i < array.length; i++) {
if (current >= array[i]) {
result.push(array[i]);
current -= array[i];
} else if (current === 0) {
result.push(array[i]);
} else {
result.push(current, array[i] - current);
current = 0;
}
}
return result;
}
Comments
Some of these answers are a bit overcomplicated. If you use a recursive function, you can do this in just two lines of code.
const pie_values = [20,10,5,5,10];
// The challenge is to transform the above array based on integer input.
// (5) => [5, 15, 10, 5, 5, 10]
// (21) => [20, 1, 9, 5, 5, 10]
// (31) => [20, 10, 1, 4, 5, 10]
function reshape(num, vals) {
if (num < vals[0]) return [num, vals[0] - num, ...vals.slice(1)];
return [vals[0], ...reshape(num - vals[0], vals.slice(1))];
}
console.log(reshape(5, pie_values))
console.log(reshape(21, pie_values))
console.log(reshape(31, pie_values))
The key is realizing that if the amount you need to take is less than the next value, then you can take it from that next value and the remainder of the array will stay the same.
But if you need to take more than what's available, take as much as you can get from the first value, and then take that much less from the remainder of the array.
EDIT: Note that if the number you give is larger than the sum of all the pie values, this will recurse infinitely (leading to a stack overflow). To be totally safe, you should ensure that the value is less than the total sum before calling reshape.
Comments
You want something like that ? (Works with the examples you've given)
function getPieValues(integer_input) {
"use strict";
let final_arr = pie_values,
array_sum = pie_values.reduce((pv, cv) => pv + cv , 0);
if(integer_input !== 0 && integer_input !== array_sum) { // For the cases 50 and 0, the array won't be modified
for(let i = 0; i < pie_values.length; i++) {
if(pie_values[i] < integer_input) { // if the prompted number is bigger than the current value, we keep up
integer_input -= pie_values[i];
} else { // When it becomes smaller, we add the remainder at the front of the current value, then we modify the next value, and finally we break it so that it doesn't happen next
final_arr.splice(i, 0, integer_input);
final_arr[i+1] -= integer_input;
break;
}
}
}
return final_arr;
}
Edit : Made it a function, and made it work with 0 and 50 (sorry, first post ;-) )
Comments
This one's pretty simple and efficient. It doesn't iterate the whole array, only up to the point it needs to.
const pie_values = [20,10,5,5,10];
function pied(n) {
var i = 0;
var total = pie_values[0];
while (total < n && i < pie_values.length) {
i++;
total += pie_values[i];
}
if (i < pie_values.length) {
var diff = total - n;
if (diff > 0 && n > 0) {
return [].concat(
pie_values.slice(0, i), // the part of the array up to i
pie_values[i] - diff, // the amount we used of the last element we needed
diff, // the amount left over
pie_values.slice(i + 1) // the rest of the array after i
);
} else {
// just return a copy of the original array
return pie_values.slice();
}
} else {
// n was greater than the total of all elements of the array
return "went over";
}
}
console.log(pied(5));
Comments
Using vanilla Javascript for-loop
Look at this code snippet
const pie_values = [20, 10, 5, 5, 10];
var fn = (input) => {
let array = [];
for (var i = 0; i < pie_values.length; i++) {
var n = pie_values[i];
let calc = n - input;
if (calc > 0) {
array.push(n - calc); // Push how many used, i.e n = 20, input = 10.
array.push(calc); // Push the remaining after subtraction.
array = array.concat(pie_values.slice(i + 1)); // Add the remaining values from 'pie_values'
return array;
} else {
array.push(n); // Push all this number because was insufficient, i.e n = 20, input = 30
input = Math.abs(calc); // Remaining for the next iteration.
}
}
return array;
};
console.log(fn(5));
console.log(fn(21));
console.log(fn(31));
console.log(fn(0));
console.log(fn(50));
Comments
const pie_values = [20,10,5,5,10];
function rebaseArr(input){
var retArr = [];
var total = 0;
let isDone = false;
for(var i in pie_values){
let currentVal = pie_values[i];
total += currentVal;
if(total > input && !isDone){
let rem = total - input;
let rem1 = currentVal - rem;
rem1 !== 0 ? retArr.push(rem1) : 0;
retArr.push(rem);
isDone = true;
} else {
retArr.push(currentVal);
}
}
return retArr;
}
console.log(rebaseArr(31));
console.log(rebaseArr(1));
console.log(rebaseArr(10));
Can you please try with above code. Hope it helps :)
Comments
I wouldn't normally advise recursion in JS however just for fun you may implement an Haskellesque pattern matching by using spread and rest operators through destructuring and may come up with something like below;
It wasn't clear to me what to do when the difference is zero so being a remarkably lazy person i choose to do nothing. (Last test won't return [20,10,5,0,5,10])
var extend = ([x,...xs],n) => n && x ? x > n ? [n, x-n, ...xs]
: [x, ...extend(xs, n-x)]
: [x,...xs],
pvs = [20,10,5,5,10];
console.log(extend(pvs,5));
console.log(extend(pvs,21));
console.log(extend(pvs,31));
console.log(extend(pvs,35));
.as-console-wrapper {
max-height : 100% !important
}