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)
Related
Say I have 2 lists with identical items that I've shuffled like below:
listA = [1, 3, 2];
listB = [2, 3, 1];
I want to make sure that list items of the same index don't match. So I wouldn't want listA[1] to match with listB[1]. How do I randomize both lists so that this doesn't occur?
There is probably a more elegant way to do this, but the code below should work, even for arrays of different sizes. It first checks whether it's possible to get the uniqueness you're looking for, and if so it goes into a while loop to continuously shuffle the larger of the two arrays (in place) until it finds a solution.
// First, set up utility functions
function shuffleArray(array) {
for (let i = array.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[array[i], array[j]] = [array[j], array[i]];
}
}
function smallerAndOther(arr1, arr2) {
const smallerArr = arr1.length < arr2.length ? arr1 : arr2;
const otherArr = smallerArr === arr1 ? arr2 : arr1;
return [smallerArr, otherArr];
}
function anyEqualIdx(arr1, arr2) {
const [smallerArr, otherArr] = smallerAndOther(arr1, arr2);
for (let i of smallerArr.keys()) {
if (smallerArr[i] === otherArr[i]) return true;
}
return false;
}
function getCount(array, value) {
return array.filter((v) => (v === value)).length;
}
// Now for the real stuff
function sufficientUnique(arr1, arr2) {
const [smallerArr, otherArr] = smallerAndOther(arr1, arr2);
for (let num of new Set([...smallerArr])) {
if (otherArr.length - getCount(otherArr, num) < getCount(smallerArr, num)) {
return false;
}
}
return true;
}
function shuffleUniqueIdxs(arr1, arr2) {
if (!sufficientUnique(arr1, arr2)) {
console.log("Error: Not enough unique values to meet constraint.");
return;
}
const largerArr = arr1.length > arr2.length ? arr1 : arr2;
while (anyEqualIdx(arr1, arr2)) {
shuffleArray(largerArr);
}
console.log("Success: ", arr1, arr2);
}
// Testing
let listA = [1, 3, 2];
let listB = [2, 3, 1];
shuffleUniqueIdxs(listA, listB);
listA = [7, 5, 5, 3, 9, 9, 1];
listB = [3, 5, 5];
shuffleUniqueIdxs(listA, listB);
listA = [1, 1, 1];
listB = [2, 1, 1];
shuffleUniqueIdxs(listA, listB); // shows error message
listA = [1, 1, 1, 1, 1];
listB = [2, 2, 2, 2, 2];
shuffleUniqueIdxs(listA, listB);
listB = [99, 9, 9, 9, 9, 9, 9, 9, 99, 88, 8, 8, 8, 8, 8, 7, 7, 6, 65, 5, 5, 5, 4]
listA = [9, 9, 9, 9, 9, 9, 8, 8, 8, 8, 8, 8, 7, 7, 7, 7, 7, 7, 6];
shuffleUniqueIdxs(listA, listB);
Here's one solution. It first individually shuffles both arrays, then it looks for repeated entries and randomly moves those around. Note that this solution only works for arrays of the same size. It is also intended to be used on arrays where most elements are unique (otherwise, it might get stuck randomly moving things around for a while).
const randIntBetween = (left, right) => left + Math.floor(Math.random() * (right - left));
function shuffle(array) {
array = [...array];
for (let i = 0; i < array.length; ++i) {
const newIndex = randIntBetween(i, array.length);
[array[i], array[newIndex]] = [array[newIndex], array[i]];
}
return array;
}
function randomlyMoveRepeatedEntries(array, comparisonArray) {
array = [...array];
const indicesToCheck = new Set(array.map((_, i) => i));
while (indicesToCheck.size) {
const { value: index } = indicesToCheck.values().next();
if (array[index] !== comparisonArray[index]) {
indicesToCheck.delete(index);
continue;
}
const newIndex = randIntBetween(index, array.length);
[array[index], array[newIndex]] = [array[newIndex], array[index]];
indicesToCheck.add(newIndex);
}
return array;
}
// ----- Example Usage ----- //
const listA = shuffle([1, 2, 3, 4, 5]);
const listB = randomlyMoveRepeatedEntries(shuffle([1, 2, 3, 4, 5]), listA);
console.log(listA.join(', '));
console.log(listB.join(', '));
i have 2 rows that i need to check in java script if they are symmetrical
row 1 [2, 7, 9, 9, 7, 2] row 2 [5 7 3 3 7 5] how would you do it ?
var r = [[5, 7, 3, 3, 7, 5], [2, 7, 9, 9, 7, 2]];
function isSymmetric(r) {
// convert to object
var rel = {}
for (var i = 0; i < r.length; i++) {
if (!(r[i][0] in rel)) rel[r[i][0]] = {};
rel[r[i][0]][r[i][1]] = true;
}
// Test to see if opposite relation is in object
for (var a in rel) {
for (var b in rel[a]) {
if (!rel[b] || !rel[b][a]) return false;
}
}
return true;
}
console.log(isSymmetric(r));
You could do something like this:
let isSymmetric = arr => {
for(var i=0; i < arr.length; i++) {
if(arr[i] !== arr[arr.length - (i+1)])
return false
}
return true
}
console.log(isSymmetric([5, 7, 3, 3, 7, 5]))
console.log(isSymmetric([1, 7, 9, 9, 7, 2]))
The idea is to loop through the array and for each index compare with its "sibling" from the right side. If one is not the same them return false.
You can start from 0th index and compare the value with its symmetric pair (length - 1 - i), and if they are not same then return false. You should stop at the middle length / 2:
let values = [
[5, 7, 3, 3, 7, 5],
[2, 7, 9, 1, 9, 7, 2],
[5, 7, 3, 3, 7, 1]
];
function isSymmetric(arr) {
for (let i = 0; i < arr.length / 2; i++) {
if (arr[i] !== arr[arr.length - 1 - i]) {
return false;
}
}
return true;
}
values.forEach(v =>
console.log(isSymmetric(v))
);
Use lodash.
var row = [2, 7, 9, 9, 7, 2];
var halves = _.chunk(row, _.ceil(row.length / 2));
var result = _.isEqual(halves[0], _.reverse(halves[1]));
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]
first question on stackoverflow, i'm struggling with this algorithm. This is supposed to slice my array in 5 like "[[0, 1], [2, 3], [4, 5], [6, 7], [8]]" but all i got is "[ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ], [ 6, 7, 8 ] ]"
function chunkArrayInGroups(arr, size) {
var newArr = [];
console.log(Math.floor(arr.length / size));
for (i = 0; i <= (Math.floor(arr.length / size)) + 1; ++i) {
var cut = size;
newArr.push(arr.splice(0, cut));
}
if (arr.length > 0) {
newArr.push(arr.splice(0, size + (arr.length - size)));
}
return newArr;
}
chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
// expected - [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
If you have any tips about the way of asking questions, i'd be happy to receive any advice !
Use a simple for loop with Array#slice, because slice doesn't change the length of the original array:
function chunkArrayInGroups(arr, size) {
var chunked = [];
for(var i = 0; i < arr.length; i += size) { // increment i by the size
chunked.push(arr.slice(i, i + size));
}
return chunked;
}
var result = chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
console.log(result);
Since you are removing element using Array#splice the array length get decreased so instead of calculating the range cache the range for the for loop condition. Although use Math.ceil and avoid the unnecessary if statement.
function chunkArrayInGroups(arr, size) {
var newArr = [],
range = Math.ceil(arr.length / size);
for (i = 0; i < range; i++) {
newArr.push(arr.splice(0, size));
}
return newArr;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));
Check this out.
function chunkArrayInGroups(arr, size) {
newArr = [];
for (i=0,j=arr.length; i<j; i+=size) {
newArr.push(arr.slice(i,i+size));
}
return newArr;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));
// expected - [[0, 1], [2, 3], [4, 5], [6, 7], [8]]
Another way to do it is Array#reduce:
function chunkArrayInGroups(arr, size) {
return arr.reduce(function (accum, elem) {
var curr = accum[accum.length - 1];
if (curr.length < size) curr.push(elem); else accum.push([elem]);
return accum;
}, [[]]);
}
var result = chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2);
console.log( JSON.stringify(result) );
Welcome to SO. Here is how I would do that. Let me know if you have any questions about this approach.
function chunkArrayInGroups(arr, size) {
var newArr = [];
while(arr.length > 0){
newArr.push(arr.splice(0, size));
}
return newArr;
}
Functionally you can do as follows;
function chunkArrayInGroups(a,g){
return Array(Math.ceil(a.length/g)).fill()
.map((_,i) => [a[g*i]].concat(a.slice(g*i+1, g*i+g)));
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8];
result = [];
result = chunkArrayInGroups(arr,2);
console.log(JSON.stringify(result));
result = chunkArrayInGroups(arr,3);
console.log(JSON.stringify(result));
result = chunkArrayInGroups(arr,4);
console.log(JSON.stringify(result));
You could use a while loop and splice the length of the wanted size for a grouped array.
function chunkArrayInGroups(array, size) {
var result = [];
while (array.length) {
result.push(array.splice(0, size));
}
return result;
}
console.log(chunkArrayInGroups([0, 1, 2, 3, 4, 5, 6, 7, 8], 2));