I'd like to find a way to combine in an array all possiblities between four percentages.
Results wanted :
possibilities = [[100,0,0,0],[99,1,0,0],[99,0,1,0],...,[0,0,1,99],[0,0,0,100]]
I'm using this function but it's very slow and doesn't seem to generate all possibilities.
combinePossibilities : function(a, min, max) {
var deferred = $q.defer();
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i){
rv['fund'+i] = arr[i];
}
return rv;
}
var fn = function(n, src, got, all) {
if (n === 0) {
if (got.length > 0) {
var total = 0;
angular.forEach(got, function(value){
total += value; //GET TOTAL OF THE COMBINATION
});
if(total === 100){
all.push(toObject(got));
}
}
return;
}
for (var j = 0; j < src.length; j++) {
fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all);
}
return;
};
var all = [];
for (var i = min; i <= max; i++) {
console.log(a);
fn(i, a, [], all);
}
deferred.resolve(all);
return deferred.promise;
}
I found this function here Find all possible subset combos in an array? and modified it to only take in my array results equal to 100%.
Any clue ?
Thank you.
This proposal is a functional recursive function, which takes sum and length. It returns an array with arrays of the combined values from the sum to zero.
function combine(sum, length, part) {
var result = [],
i = sum;
part = part || [];
if (length === 1) {
return [part.concat(sum)];
}
if (length === 0) {
return [part];
}
do {
result = result.concat(combine(sum - i, length - 1, part.concat(i)));
} while (i--);
return result;
}
How it works:
It starts with the given sum and length and an empty result set, as well as an iterator variable i with the value of sum.
If part is not given, then an empty array is assigned.
Now follows some checks for the (leftover) length and their special treatment, if it is
1: This is last iteration and the sum is left, only. Then return the partial result part concatenated by sum in an array.
0: No more iteration, then return the partial result part in an array.
If the length is neither 1 nor 0, then interate over the sum to zero.
The call of combine takes the reduced sum, the decremented length and the partial result part with the value of i.
The result of the call of combine() is concatenated to the result set.
Example for combine(5, 3):
length: 21
[
[5, 0, 0],
[4, 1, 0],
[4, 0, 1],
[3, 2, 0],
[3, 1, 1],
[3, 0, 2],
[2, 3, 0],
[2, 2, 1],
[2, 1, 2],
[2, 0, 3],
[1, 4, 0],
[1, 3, 1],
[1, 2, 2],
[1, 1, 3],
[1, 0, 4],
[0, 5, 0],
[0, 4, 1],
[0, 3, 2],
[0, 2, 3],
[0, 1, 4],
[0, 0, 5]
]
Working code with two examples:
combine(5, 3)
combine(10, 4)
function combine(sum, length, part) {
var result = [],
i = sum;
part = part || [];
if (length === 1) {
return [part.concat(sum)];
}
if (length === 0) {
return [part];
}
do {
result = result.concat(combine(sum - i, length - 1, part.concat(i)));
} while (i--);
return result;
}
function print(array) {
document.write('<pre>length: ' + array.length + '\n' + JSON.stringify(array, 0, 4) + '</pre>');
}
print(combine(5, 3));
print(combine(10, 4));
Related
I am taking an excercise on codewars:
Given a list of integers and a single sum value, return the first two
values (parse from the left please) in order of appearance that add up
to form the sum.
Example:
sum_pairs([10, 5, 2, 3, 7, 5], 10)
# ^-----------^ 5 + 5 = 10, indices: 1, 5
# ^--^ 3 + 7 = 10, indices: 3, 4 *
# * entire pair is earlier, and therefore is the correct answer
== [3, 7]
What do you think entire pair is earlier means? IMO if the sum of it's indexes is smallest. Now based on this assumption I made my solution and one test fails:
var sum_pairs=function(ints, s){
let i = 0;
let pair = [0, 0];
let ind = [100, 100]
let found = false;
function loop(i) {
if (i > ints.length) return pair;
ints.slice(i).forEach((curr, idx) => {
ints.slice(i+1).some((num, i) => {
let sum = curr + num;
let prevIndicies = ind[0] + ind[1];
if(sum === s && prevIndicies > idx + i) {
ind = [idx, i];
pair = [curr, num];
found = true;
return true;
}
})
})
i += 1;
loop(i)
}
loop(0)
if (found) {
return pair
}
return undefined;
}
console.log(sum_pairs([1,4,8,7,3,15], 8))
Test returns error that [1, 7] is expected.
I'm pretty sure what it means is they want the second element to be as leftward in the list as possible. For example, for
l5= [10, 5, 2, 3, 7, 5];
when trying to find a sum of 10, the desired output is
[3, 7]
[10, 5, 2, 3, 7, 5];
^ ^
instead of
[5, 5]
[10, 5, 2, 3, 7, 5];
^ ^
because the last element in [3, 7], the 7, came before the second 5.
This code seems to pass all test cases - iterate in a triangular fashion, starting at indicies [0, 1], [0, 2], [1, 2], [0, 3], [1, 3], [2, 3], ...:
const sum_pairs = function(ints, s){
const { length } = ints;
for (let i = 1; i < length; i++) {
for (let j = 0; j < i; j++) {
if (ints[i] + ints[j] === s) return [ints[j], ints[i]];
}
}
}
const sum_pairs=function(ints, s){
const { length } = ints;
for (let i = 1; i < length; i++) {
for (let j = 0; j < i; j++) {
if (ints[i] + ints[j] === s) return [ints[j], ints[i]];
}
}
}
l1= [1, 4, 8, 7, 3, 15];
l2= [1, -2, 3, 0, -6, 1];
l3= [20, -13, 40];
l4= [1, 2, 3, 4, 1, 0];
l5= [10, 5, 2, 3, 7, 5];
l6= [4, -2, 3, 3, 4];
l7= [0, 2, 0];
l8= [5, 9, 13, -3];
console.log(sum_pairs(l1, 8))
console.log(sum_pairs(l2, -6))
console.log(sum_pairs(l3, -7))
console.log(sum_pairs(l4, 2))
console.log(sum_pairs(l5, 10))
console.log(sum_pairs(l6, 8))
console.log(sum_pairs(l7, 0))
console.log(sum_pairs(l8, 10))
It means that you go from left to right and take the first matching pair, and since 7 is the first element that creats a pair (going from the left) 3 and 7 is the first pair.
I would solve it a bit easier:
function sum_pairs(arr, target) {
let old = [];
let result = [];
arr.some((el) => {
let found = old.find((oldEl) => oldEl + el === target);
if (found) return result = [found, el];
old.push(el);
})
return result;
}
sum_pairs([10, 5, 2, 3, 7, 5], 10);
Edit: an explaination. I loop over all elements in the array searching for a match i all the elements I have passed. If I find a match I remember it and break out of the loop by returning a "truthy" value. (That is just how .some() works.) Finally if I have not found a match I add the element to my list of old elements and go on to the next.
function sum_pair(arr,sum){
let result = [];
arr.forEach((i, j)=>{
if(i+arr[j+1]===sum){
console.log(i,arr[j+1], i+arr[j+1])
}
})
}
sum_pair([0, 3, 7, 0, 5, 5],10)
I am working on a javascript function which takes an array of integers and a target as arguments. The task is to find the first pair of integers in the array whose sum is equal to the target. I have tried this several different ways, but I keep getting a timeout error for larger input arrays. Can someone please give me some pointers on how to better optimize this code? Thanks!
var sum_pairs = function(ints, s){
var r = [];
var a = true;
var l = ints.length;
for(var j = 0; j < l; j++){
if(a){
for(var i = 0; i < j; i++){
if(ints[j] + ints[i] == s){
r[0] = ints[i];
r[1] = ints[j];
a = false;
break;
}
}
}
else{
console.log('breaking');
break;
}
}
return r[0] == null ? null : r;
}
You could use some speeding mechanisms, like
single loop,
hash table for visited values
variable a for element array[i]
very short variable names (just kidding)
Long list needs 153 ms.
var sum_pairs = function (array, s) {
var a, i,
hash = Object.create(null);
for (i = 0; i < array.length; i++) {
a = array[i];
if (hash[s - a]) {
return [s - a, a];
}
if (!hash[a]) {
hash[a] = true;
}
}
};
console.log(sum_pairs([11, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(sum_pairs([0, 0, -2, 3], 2)); // undefined
console.log(sum_pairs([10, 5, 2, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([1, 2, 3, 4, 1, 0], 2)); // [1, 1]
console.log(sum_pairs([1, -2, 3, 0, -6, 1], -6)); // [0, -6]
console.log(sum_pairs([0, 2, 0], 0)); // [0, 0]
console.log(sum_pairs([5, 9, 13, -3], 10)); // [13, -3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
For each number that we encounter while iterating the array, we add that number's expected partner target - number into a Set. As soon as we encounter a number that is already in our set, we know that its partner has already been encountered and return this pair as the solution:
// Return the first two values of 'numbers' summing up to 'target':
function sum_pairs(numbers, target) {
let paired = new Set();
for (let number of numbers) {
if (paired.has(number)) return [target - number, number];
paired.add(target - number);
}
}
// Examples:
console.log(...sum_pairs([9, 3, 7, 5, 1], 10)); // [3, 7]
console.log(...sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(...sum_pairs([9, 3, 6, 4, 1], 10)); // [6, 4]
This implementation has a linear runtime complexity and is therefore faster for long input arrays, but it comes with an additional memory cost.
If you are going for raw speed, replace the for-of loop with a traditional for-loop and the let variable binding with a var declaration.
I am doing a codewars problem, the instructions are as follows: Given a list of integers and a single sum value, return the first two values (parse from the left please) in order of appearance that add up to form the sum.
The solution works, but it is too slow for long arrays, how would someone do this without using two for loops? I have been trying to reduce the time complexity, but I am at a loss at how to accomplish this when I need to look at all possible pairs.
function sumPairs(ints, s){
var lowestIdx1 = Infinity;
var lowestIdx2 = Infinity;
for (var i = 0; i < ints.length-1; i++) {
var cur = ints[i]
for (var k = i+1; k < ints.length; k++) {
var next = ints[k]
if(cur + next === s){
if(i <= lowestIdx1 && k <= lowestIdx1 || i <= lowestIdx2 && k <=lowestIdx2){
lowestIdx1 = i
lowestIdx2 = k
}
}
}
}
if(lowestIdx1 !== Infinity){
return [ints[lowestIdx1], ints[lowestIdx2]]
}
}
To be more clear on the problem here are some sample input outputs:
sum_pairs([11, 3, 7, 5], 10)
# ^--^ 3 + 7 = 10
== [3, 7]
sum_pairs([4, 3, 2, 3, 4], 6)
# ^-----^ 4 + 2 = 6, indices: 0, 2 *
# ^-----^ 3 + 3 = 6, indices: 1, 3
# ^-----^ 2 + 4 = 6, indices: 2, 4
# * entire pair is earlier, and therefore is the correct answer
== [4, 2]
sum_pairs([0, 0, -2, 3], 2)
# there are no pairs of values that can be added to produce 2.
== undefined
You could use some speeding mechanisms, like
single loop,
hash table for visited values
variable a for element array[i]
Long list of Sum of Pairs on Codewars needs 153 ms.
var sum_pairs = function (array, s) {
var a, i,
hash = Object.create(null);
for (i = 0; i < array.length; i++) {
a = array[i];
if (hash[s - a]) {
return [s - a, a];
}
if (!hash[a]) {
hash[a] = true;
}
}
};
console.log(sum_pairs([11, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([4, 3, 2, 3, 4], 6)); // [4, 2]
console.log(sum_pairs([0, 0, -2, 3], 2)); // undefined
console.log(sum_pairs([10, 5, 2, 3, 7, 5], 10)); // [3, 7]
console.log(sum_pairs([1, 2, 3, 4, 1, 0], 2)); // [1, 1]
console.log(sum_pairs([1, -2, 3, 0, -6, 1], -6)); // [0, -6]
console.log(sum_pairs([0, 2, 0], 0)); // [0, 0]
console.log(sum_pairs([5, 9, 13, -3], 10)); // [13, -3]
.as-console-wrapper { max-height: 100% !important; top: 0; }
The solution below runs in O(n) time, check out the steps for how it was solved:
// steps
// loop through array
// for each member
// first check if it's value in hash
// then store in hash with key as sum-member
// and value as member
// if value in hash already
// return [k,v]
function sumPairs(ints, s) {
const possible_pairs={}
// loop through array
for(let ints_i=0;ints_i<ints.length;ints_i+=1){
// for each member
let element = ints[ints_i].toString()
// first check if it's value in hash
// if value in hash already
// return [k,v]
if (possible_pairs[element]) return [parseInt(possible_pairs[element], 10), parseInt(element, 10)]
// else store in hash with key as value-member
// and value as member
possible_pairs[s-element]=element
}
return undefined ;
}
console.log(sumPairs([ 0, -6], -6)) //[0, -6]
console.log(sumPairs([10, 5, 2, 3, 7, 5], 10)) //[3, 7]
I have a number array [2, 1, 3, 4, 5, 1] and want to remove the smallest number in the list. But somehow my IF statement gets skipped.
I checked and by itself "numbers[i + 1]" and "numbers[i]" do work, but "numbers[i + 1] < numbers[i]" doesn't...
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (i = 0; i <= numbers.lenths; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
}
}
numbers.splice(smallestNumberKEY, 1);
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
You have a typo in your code, array doesn't have lenths property
function removeSmallest(numbers) {
var smallestNumberKEY = 0;
for (var i = 0; i < numbers.length - 1; i++) {
if (numbers[i + 1] < numbers[i]) {
smallestNumberKEY = i + 1;
numbers.splice(smallestNumberKEY, 1);
}
}
return numbers;
}
document.write(removeSmallest([2, 1, 3, 4, 5, 1]));
But your algorithm wont work for another array, e.g [5, 3, 1, 4, 1], it will remove a value 3 too.
You can find the min value with Math.min function and then filter an array
function removeSmallest(arr) {
var min = Math.min(...arr);
return arr.filter(e => e != min);
}
You can use Array#filter instead
function removeSmallest(arr) {
var min = Math.min.apply(null, arr);
return arr.filter((e) => {return e != min});
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1]))
Short one liner. If the smallest value exist multiple times it will only remove ONE. This may or may not be what you want.
const result = [6,1,3,1].sort().filter((_,i) => i) // result = [1,3,6]
It works by sorting and then creating a new array from the items where indeces are truthy(anything but 0)
another solution with splice and indexOf:
array = [2, 1, 3, 4, 5, 1];
function replace(arr){
arr = arr.slice(); //copy the array
arr.splice( arr.indexOf(Math.min.apply(null, arr)),1)
return arr;
}
document.write( replace(array) ,'<br> original array : ', array)
edit : making a copy of the array will avoid the original array from being modified
"Short" solution using Array.forEach and Array.splice methods:
function removeSmallest(numbers) {
var min = Math.min.apply(null, numbers);
numbers.forEach((v, k, arr) => v !== min || arr.splice(k,1));
return numbers;
}
console.log(removeSmallest([2, 1, 3, 4, 5, 1])); // [2, 3, 4, 5]
This is a proposal with a single loop of Array#reduce and without Math.min.
The algorithm sets in the first loop min with the value of the element and returns an empty array, because the actual element is the smallest value and the result set should not contain the smallest value.
The next loop can have
a value smaller than min, then assign a to min and return a copy of the original array until the previous element, because a new minimum is found and all other previous elements are greater than the actual value and belongs to the result array.
a value greater then min, then the actual value is pushed to the result set.
a value equal to min, then the vaue is skipped.
'use strict';
var removeSmallest = function () {
var min;
return function (r, a, i, aa) {
if (!i || a < min) {
min = a;
return aa.slice(0, i);
}
if (a > min) {
r.push(a);
}
return r;
}
}();
document.write('<pre>' + JSON.stringify([2, 1, 3, 2, 4, 5, 1].reduce(removeSmallest, []), 0, 4) + '</pre>');
I like this oneliner: list.filter(function(n) { return n != Math.min.apply( Math, list ) })
check it out here: https://jsfiddle.net/rz2n4rsd/1/
function remove_smallest(list) {
return list.filter(function(n) { return n != Math.min.apply( Math, list ) })
}
var list = [2, 1, 0, 4, 5, 1]
console.log(list) // [2, 1, 0, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 1, 4, 5, 1]
list = remove_smallest(list)
console.log(list) // [2, 4, 5]
I had to do this but I needed a solution that did not mutate the input array numbers and ran in O(n) time. If that's what you're looking for, try this one:
const removeSmallest = (numbers) => {
const minValIndex = numbers.reduce((finalIndex, currentVal, currentIndex, array) => {
return array[currentIndex] <= array[finalIndex] ? currentIndex : finalIndex
}, 0)
return numbers.slice(0, minValIndex).concat(numbers.slice(minValIndex + 1))
}
function sumOfPaiars(ints){
var array = [];
var min = Math.min(...ints)
console.log(min)
for(var i=0;i<ints.length;i++){
if(ints[i]>min){
array.push(ints[i])
}
}
return array
}
If you only wish to remove a single instance of the smallest value (which was my use-case, not clear from the op).
arr.sort().shift()
Here is a piece of code that is work properly but is not accepted from codewars:
let numbers = [5, 3, 2, 1, 4];
numbers.sort(function numbers(a, b) {
return a - b;
});
const firstElement = numbers.shift();
I'm not sure if the title of this question is correct or not and also not sure what the appropriate keyword to search on google.
I have an array look like:
var myArray = [1,1,2,2,2,3,4,4,4];
and I want to sort my array into:
var myArray = [1,2,3,4,1,2,4,2,4];
Please in to my expected result. the order is ascending but duplicate value will repeated on last sequence instead of put it together in adjacent keys. So the expected result grouped as 1,2,3,4 1,2,4 and 2,4.
Thank you for your help and sorry for my bad English.
This code works. But it may exist a better solution.
// We assume myArray is already sorted
var myArray = [1,1,2,2,2,3,4,4,4],
result = [];
while (myArray.length) {
var value = myArray.shift();
// Find place
var index = 0;
while(result[index] && result[index][result[index].length - 1] == value) index++;
if(!result[index]) {
result[index] = [];
}
result[index][result[index].length] = value;
}
result.reduce(function(current, sum) {
return current.concat(sum);
});
console.log(result) // Display [1, 2, 3, 4, 1, 2, 4, 2, 4]
Here is my method using JQuery and it does not assume the array is already sorted.
It will iterate through the array and no duplicates to tempResultArray, once finished, it will then add them to the existing result and repeat the process again to find duplicates.
This is not the most efficient method, but it can be handled by one function and does not require the array to be sorted.
var myArray = [1,1,2,2,2,3,4,4,4],result = [];
while (myArray && myArray.length) {
myArray = customSort(myArray);
}
console.log(result);
function customSort(myArray){
var tempResultArray = [], tempMyArray = [];
$.each(myArray, function(i, el){
if($.inArray(el, tempResultArray ) === -1){
tempResultArray.push(el);
}else{
tempMyArray.push(el);
}
});
tempResultArray.sort(function(a, b){return a-b});
$.merge( result,tempResultArray)
return tempMyArray;
}
JSFiddle
This proposal features a straight forward approach with focus on array methods.
function sprout(array) {
return array.reduce(function (r, a) {
!r.some(function (b) {
if (b[b.length - 1] < a) {
b.push(a);
return true;
}
}) && r.push([a]);
return r;
}, []).reduce(function (r, a) {
return r.concat(a);
});
}
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 2, 2, 3, 4, 4, 4]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 2, 3, 7, 7, 7]), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(sprout([1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 6, 6, 6, 7]), 0, 4) + '</pre>');
here's another solution:
var myArray = [1, 1, 2, 2, 2, 3, 4, 4, 4];
function mySequelArray(arr) {
var res = arguments[1] || [];
var nextVal;
var min = Math.min.apply(null, arr);
if (res.length > 0) {
nextVal = arr.filter(function (x) {
return x > res[res.length - 1]
}).sort()[0] || min;
} else {
nextVal = min;
}
res.push(nextVal);
arr.splice(arr.indexOf(nextVal), 1);
return (arr.length > 0) ? mySequelArray(arr, res) : res;
}
console.log(mySequelArray(myArray))
fiddle
My best approach will be to split your array into separate arrays for each repeated value, then arrange each separate array and join altogether.
UPDATED:
I wrote a quick code sample that should work if the same number in inputArray is not given more than twice. You could improve it by making it recursive thus creating new arrays for each new number and removing the limitation. Had some free time so i re-wrote a recursive function to sort any given array in sequence groups like you wanted. Works like a charm, inputArray does not need to be sorted and doesn't require any libraries. Jsfiddle here.
var inputArray = [3, 4, 1, 2, 3, 1, 2, 4, 1, 2, 5, 1];
var result = sortInSequence(inputArray);
console.log(result); //output: [1, 2, 3, 4, 5, 1, 2, 3, 4, 1, 2, 1]
function sortInSequence(inputArray){
var inputArraySize = inputArray.length,
tempArray = [], //holds new array we are populating
sameValuesArray = [], //holds same values that we will pass as param in recursive call
rSorted = []; //init sorted array in case we have no same values
for(var i = inputArraySize; i > 0; i--){
var value = inputArray.pop();
tempArray.push(value);
var counter = 0,
tempArraySize = tempArray.length;
for(var j = 0; j < tempArraySize; j++){
if(tempArray[j] == value){
counter++;
}
}
if(counter == 2){
//value found twice, so remove it from tempArray and add it in sameValuesArray
var sameValue = tempArray.pop();
sameValuesArray.push(sameValue);
}
}
if(sameValuesArray.length > 0){
rSorted = sortInSequence(sameValuesArray);
}
tempArray.sort();
return tempArray.concat(rSorted);
}