Using an array of numbers to add up to a specific number - javascript

I've been trying to find out an efficient method of finding multiple numbers from an array that add up to a given number. In this instance I'm trying to find 3 numbers that total a target number.
I've got a basic working example below but unfortunately the recursive loop fails, it looks like there's an issue with it constantly looping. Ideally it would find the first possible answer and return it, but when it can't find an answer it gets stuck in the loop and breaks the browser.
Warning: the below code will break due to a memory leak:
let array = [5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2,5,6,3,3,6,67,2,2,6,7,7,2,1,3,4,5,67,7,4,2];
function findSums(arr, target, count) {
var result = [];
function recurse(start, leftOver, selection) {
if (leftOver < 0) return; // failure
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return;
}
for (var i = start; i < arr.length; i++) {
recurse(i, leftOver-arr[i], selection.concat(arr[i]));
}
}
recurse(0, target, []);
return result;
}
// Demo
$('#input').on('blur', function(e){
let value = parseInt(e.target.value);
let result = findSums(array, value, 3);
if(result.length > 0){
$('#output').text(result[0]);
} else {
$('#output').text('Nothing found');
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Input Number Below</h1>
<input id="input" type="number" />
<code id="output" ></code>

Well, it didn't break, when I tested, but still here are some tips:
You should set additional limitation to the count. You are making to much extra calls. When your function deals with really big sum, small numbers and small count it will call itself again until it reaches or overflows the desired sum, and only after that it will check current count. So you should add
if (selection.length > count) return;
Also. As I see there are many duplicates in your array, so I assume, that usage of the same number is allowed, but only if it is taken from another index. In your loop you are calling next recurse with the same start index. I think, you need
for (var i = start; i < arr.length; i++) {
recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
}
And finally. This will not influence the recursive part of an algorithm, but maybe you'd like to filter out same results, or filter out your array to remove all duplicates.
Hope this helps.
Edit: sorry, missed the part about first possible solution. Here is the way to achieve this:
function recurse(start, leftOver, selection) {
if (leftOver < 0) return false; // failure
if (selection.length > count) return false;
if (leftOver === 0 && selection.length == count) {
result.push(selection); // add solution
return true;
}
for (var i = start; i < arr.length; i++) {
var res = recurse(i + 1, leftOver-arr[i], selection.concat(arr[i]));
if (res) return true;
}
}

Related

How can I check that a specific key in an array is empty at every index?

So I have an array of objects to iterate over, on every iteration I need to check if a key named blocks has or has no length.
If all of the blocks keys are empty (length === 0) then I need to do an operation, otherwise do not. Get it? If ONNLY ONE blocks key has a length > 0 then the operation should be avoided.
for (let i = 0; i < blockColumns.length; i++) {
if (!blockColumns[i].blocks.length) {
// TRIGGER OPERATION
}
}
In the code above the problem is that the operation will be trigger when at least one blocks key has no length, which what I don't need. The operation should be trigger ONLY when all of the blocks keys have length === 0.
Use Array.every() to check that every item has a 0 length (!0 === true). If all items have 0 length, the every would return true. If a false is encountered (ie length > 0), every would return false immediately.
const allEmpty = blockColumns.every(o => !o.blocks.length)
if(allEmpty) doSomething()
This sounds like something that Array.prototype.every would be useful for:
if (blockColumns.every((el) => el?.blocks?.length === 0)) {
// Do your operation
}
I've used optional chaining here since I'm not sure if there might be a case where an element doesn't have a blocks property, or if that property may not have a length property. But depending on how confident you can be in your data structure, you may not need to do that.
well then you can do
x = 0
for (let i = 0; i < blockColumns.length; i++) {
if (!blockColumns[i].blocks.length > 0) {
x+=1;
}
}
if(x == blockColumns.length){
//do something
}
this is a very simple way of doing it and i don't think that it's efficient at all but it gets the job done for now :D
let doOperation = true;
for (let i = 0; i < blockColumns.length; i++) {
if (blockColumns[i].blocks.length > 0) {
doOperation = false
}
}
if (doOperation) {
//Some operation
}
not sure if I follow but would this work?

How to simplify javascript program?

I would love to minimize the program.
Maybe putting p1-16 in one line of code, same with count and gefunden?
Since my language skills are minimal I can't find the right information.
It would also be great if there was a way to minimize the if else statements in search hits pdf.
Right now I do the code by hand to add new pdfs, as in search hits pdf1 to pdf2. Any easier way would greatly help me.
function Suche(str){
p1=document.getElementById('pdf1').innerHTML;
p2=document.getElementById('pdf2').innerHTML;
p3=document.getElementById('pdf3').innerHTML;
p4=document.getElementById('pdf4').innerHTML;
p5=document.getElementById('pdf5').innerHTML;
p6=document.getElementById('pdf6').innerHTML;
p7=document.getElementById('pdf7').innerHTML;
p8=document.getElementById('pdf8').innerHTML;
p9=document.getElementById('pdf9').innerHTML;
p10=document.getElementById('pdf10').innerHTML;
p11=document.getElementById('pdf11').innerHTML;
p12=document.getElementById('pdf12').innerHTML;
p13=document.getElementById('pdf13').innerHTML;
p14=document.getElementById('pdf14').innerHTML;
p15=document.getElementById('pdf15').innerHTML;
p16=document.getElementById('pdf16').innerHTML;
p17=document.getElementById('pdf17').innerHTML;
gefunden1=0;
gefunden2=0;
gefunden3=0;
gefunden4=0;
gefunden5=0;
gefunden6=0;
gefunden7=0;
gefunden8=0;
gefunden9=0;
gefunden10=0;
gefunden11=0;
gefunden12=0;
gefunden13=0;
gefunden14=0;
gefunden15=0;
gefunden16=0;
gefunden17=0;
count1=0;
count2=0;
count3=0;
count4=0;
count5=0;
count6=0;
count7=0;
count8=0;
count9=0;
count10=0;
count11=0;
count12=0;
count13=0;
count14=0;
count15=0;
count16=0;
count17=0;
searchstring=str;
//Search Hits PDF1
endsearch=p1.length;
weiter=1;
if(p1.indexOf(str)>-1){
gefunden1=1;
pos1=p1.indexOf(str)+searchstring.length;
count1=count1+1;}
else{weiter=0;}
for(i = 1; i <=10; i++){
if(weiter==1){
if(p1.indexOf(str,pos1)>-1){
pos2=p1.indexOf(str,pos1)+searchstring.length;
if (pos2<=endsearch){
if(count1<10){
count1=count1+1;
pos1=pos2;}
else{
count1="Mehr als 10";
pos1=pos2;}}
else{
weiter=0;}}
else{
weiter=0;}}}
//Search Hits Pdf2
endsearch=p2.length;
weiter=1;
if(p2.indexOf(str)>-1){
gefunden2=1;
pos1=p2.indexOf(str)+searchstring.length;
count2=count2+1;}
else{weiter=0;}
for(i = 1; i <=10; i++){
if(weiter==1){
if(p2.indexOf(str,pos1)>-1){
pos2=p2.indexOf(str,pos1)+searchstring.length;
if (pos2<=endsearch){
if(count1<10){
count2=count2+1;
pos1=pos2;}
else{
count2="Mehr als 10";
pos1=pos2;}}
else{
weiter=0;}}
else{
weiter=0;}}}
and so on....
Why not use an array called p?
const p = []
for (let i=1; i<18; i++) {
p.push(document.getElementById(`pdf${i}`).innerHTML)
}
You can do the same for gefunden and count. The rest of your code, if repetitive, could go in a function and be called in another for loop.
I agree that this should be on code review. But you have a working code and ask how to make it better. So here you go.
replace all those variables that have the format variableN with an array. As soon as you have such a naming format you most of the time either want to use an array or change the name.
And you definitely want to clean up that function that searches for the occurrence of the given string.
Always define variables using const or let. And add comments to the code.
If something reflects a boolean, then use one instead of 0 or 1.
Make use of comments, that will also help others when looking at your code (and it also helps you if you look at your code after a while).
Use variables instead of magic numbers like 10.
and even if your preferred language is not the one used of the programming language, you should stick with the one the programming language use.
So here is a reworked version of your code:
// use an options parameter to make the function more flexible
function search(str , options) {
// destructuring the options into variables
const {maxCount, pdfCount} = options;
const items = [];
for (let i = 1; i <= pdfCount; i++) {
items.push({
p: document.getElementById(`pdf${i}`).innerHTML,
found: false,
count: 0
})
}
items.forEach(item => {
let count = 0;
let currentPosition = 0; // position where to start searching
let foundAtPosition;
// do-while loop to do at least one search
do {
foundAtPosition = item.p.indexOf(str, currentPosition);
// check if we found an occurence
if (foundAtPosition != -1) {
// increase the count
count++;
// set the current position after the found occurence
currentPosition = foundAtPosition + str.length;
}
// if we found more then maxCount we can leave the loop
if (count > maxCount) {
break;
}
// only continue the loop when something was found
// you could move "count > maxCount" to the while condition
// but the main purpose of the while loop is to iterate over the content
} while (foundAtPosition != -1);
// do the composing the information to be set for the item after the for loop,
// that makes many things clearer as it is not part of the searching process
// set found to true or false by checking if count is larger then 0
item.found = count > 0;
if (count > maxCount) {
item.count = `Mehr als ${maxCount}`;
} else {
item.count = count;
}
})
return items;
}
console.dir(search('hey', {maxCount: 10, pdfCount: 3}))
<div id="pdf1">
heyheyaaheyaaahey
</div>
<div id="pdf2">
heyheyaaheyaaahey
heyheyaaheyaaahey
heyheyaaheyaaahey
</div>
<div id="pdf3">
foo
</div>
You could also utelize str.split([separator[, limit]]) as mention here How to count string occurrence in string? and utilize the limit function.
But if you do that you really need to document the item.p.split(str, maxCount+2).length - 1 construct because that's hard to understand otherwise.
// use an options parameter to make the function more flexible
function search(str , options) {
// destructuring the options into variables
const {maxCount, pdfCount} = options;
const items = [];
for (let i = 1; i <= pdfCount; i++) {
items.push({
p: document.getElementById(`pdf${i}`).innerHTML,
found: false,
count: 0
})
}
items.forEach(item => {
// use maxCount + 2 to figure out if we have more then max count subtract 1 from length to get the actual count
const count = item.p.split(str, maxCount+2).length - 1
// set found to true or false by checking if count is larger then 0
item.found = count > 0;
if (count > maxCount) {
item.count = `Mehr als ${maxCount}`;
} else {
item.count = count;
}
})
return items;
}
console.dir(search('hey', {maxCount: 10, pdfCount: 3}))
<div id="pdf1">
heyheyaaheyaaahey
</div>
<div id="pdf2">
heyheyaaheyaaahey
heyheyaaheyaaahey
heyheyaaheyaaahey
</div>
<div id="pdf3">
foo
</div>
You can use arrays.
Arrays, in a simple way, put lots of info into one variable. For example:
var this_is_an_array=[0,1,2,3,4,5]
Arrays count from 0 and onwards, so do take note to start counting from 0
More in detail at w3 schools: https://www.w3schools.com/js/js_arrays.asp

Leetcode Two sum problem question about why my code doesn't work

Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
var twoSum = function(nums, target) {
let comp = {};
for(let i =0; i<nums.length; i++){
let match = target - nums[i]
my question is why doesn't my code work if remove comp[match]>=0 and use comp[match] instead?
if(comp[match]>=0){
return [comp[match], i]
console.log(comp)
}
else{
comp[nums[i]]= i
}
console.log(comp)
}
};
Snippet:
var twoSum = function(nums, target) {
let comp = {};
for (let i = 0; i < nums.length; i++) {
let match = target - nums[i]
if (comp[match]) {
return [comp[match], i]
console.log(comp)
} else {
comp[nums[i]] = i
}
console.log(comp)
}
};
twoSum([2, 7, 11, 15], 9)
The idea behind comp is to store the indexes of the values previously seen while you're looping through your array of numbers. This means that it is possible for a key in your object to point to the index 0.
In JavaScript, 0 is considered falsy, so when put into a if statement, it will skip the if block as it is considered false, and instead, execute the else block.
if(0) {
console.log("truthy"); // doesn't execute
} else {
console.log("falsy");
}
So, if you were to use if(comp[match]) and comp[match] was to give you the index value of 0, your else block would trigger, when instead, you actually need your if block to trigger (as you have previously seen a number which you can now add with the current number). That's why the following works as expected:
if(comp[match] >= 0)
In this scenario, if comp[match] returns back the index value of 0, the code in your if-block will be triggered as needed. It is possible for comp[match] to return undefined though. In this case, your else block will trigger so your code will work fine (as undefined >= 0 is false). However, if you want to make your condition more readable you can instead use:
if(conp[match] !== undefined)

recursion on returning vectors c++

Hey guys I am trying trying to right this javascript code into c++. I am doing quick sort and everything is straight forward minus the last step.
function quickSort(arr)
{
//base case if the arr is 1 or 0 then return the array
if(arr.length === 1 || arr.length === 0)
{
return arr;
}
var pivotIndex = Math.floor(arr.length/2);
var pivotValue = arr[pivotIndex];
var before = [];
var after = [];
for(var counter = 0; counter < arr.length; counter++)
{
if(counter === pivotIndex)
continue;
if(pivotValue <= arr[counter])
{
before.push(arr[counter])
}
else
{
after.push(arr[counter])
}
}
//this step I am having trouble rewriting in c++
return quickSort(after).concat(pivotValue).concat(quickSort(before));
}
I am having a hard time rewriting the recursive step in c++. I am not sure how concat 2 vector. I tried using the insert method but I keep getting an error about invalid use of void expression.
vector<int> quickSort(vector<int> arr)
{
if(arr.size() == 1 || arr.size() == 0)
{
return arr;
}
int pivotIndex = arr.size()/2;
int pivotValue = arr[pivotIndex];
vector<int> before;
vector<int> after;
//put values in before or after the piv
for(size_t counter = 0; counter < arr.size(); counter++)
{
if(counter == pivotIndex)
continue;
if(pivotValue <= arr[counter])
before.push_back( arr[counter]);
else
after.push_back( arr[counter]);
}
return //????? not sure how to do this
}
So, you realized that your core question was "how to concatenate two vectors", and you found a right answer: using insert. Now your question is about why you were getting "an error about invalid use of void expression." (That's the assumption my answer is for, at least.)
That's because you were likely trying to do something like the following:
return quickSort(after).insert( /* stuff */ );
which is wrong. In JavaScript, array.concat returns the concatenated array. It's return type is effectively Array, and so doing return arr.concat(arr2) returns an Array because arr.concat would return an Array. Further, in JavaScript, array.concat doesn't modify the array it was called on, but rather returns a new array.
In C++, however, vector.insert (#4 in the reference) returns void. That means it returns nothing. So when you try to return the result of insert, you get that error about invalid use of a void expression. Further, in C++, vector.insert does modify the vector it was called on.
So how do you use insert in this case?
vector<int> quickSort(vector<int> arr)
{
// ...
// Sort `before` and `after`
before = quickSort(before);
after = quickSort(after);
// Modify `after` and return it.
after.push_back(pivotValue);
after.insert(after.end(), before.begin(), before.end());
return after;
}
Note: My code isn't optimal and the idea of rewriting JS in C++ is also oddly specific. My answer is to simply outline the problem asked in the question, not to give a good C++ implementation of quick sort.
To concat two vector , you can use std::merge
like:std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));

How to partition array of integers to even and odd?

I want to partition an array (eg [1,2,3,4,5,6,7,8]), first partition should keep even values, second odd values (example result: [2,4,6,8,1,3,5,7]).
I managed to resolve this problem twice with built-in Array.prototype methods. First solution uses map and sort, second only sort.
I would like to make a third solution which uses a sorting algorithm, but I don't know what algorithms are used to partition lists. I'm thinking about bubble sort, but I think it is used in my second solution (array.sort((el1, el2)=>(el1 % 2 - el2 % 2)))... I looked at quicksort, but I don't know where to apply a check if an integer is even or odd...
What is the best (linear scaling with array grow) algorithm to perform such task in-place with keeping order of elements?
You can do this in-place in O(n) time pretty easily. Start the even index at the front, and the odd index at the back. Then, go through the array, skipping over the first block of even numbers.
When you hit an odd number, move backwards from the end to find the first even number. Then swap the even and odd numbers.
The code looks something like this:
var i;
var odd = n-1;
for(i = 0; i < odd; i++)
{
if(arr[i] % 2 == 1)
{
// move the odd index backwards until you find the first even number.
while (odd > i && arr[odd] % 2 == 1)
{
odd--;
}
if (odd > i)
{
var temp = arr[i];
arr[i] = arr[odd];
arr[odd] = temp;
}
}
}
Pardon any syntax errors. Javascript isn't my strong suit.
Note that this won't keep the same relative order. That is, if you gave it the array [1,2,7,3,6,8], then the result would be [8,2,6,3,7,1]. The array is partitioned, but the odd numbers aren't in the same relative order as in the original array.
If you are insisting on an in-place approach instead of the trivial standard return [arr.filter(predicate), arr.filter(notPredicate)] approach, that can be easily and efficiently achieved using two indices, running from both sides of the array and swapping where necessary:
function partitionInplace(arr, predicate) {
var i=0, j=arr.length;
while (i<j) {
while (predicate(arr[i]) && ++i<j);
if (i==j) break;
while (i<--j && !predicate(arr[j]));
if (i==j) break;
[arr[i], arr[j]] = [arr[j], arr[i]];
i++;
}
return i; // the index of the first element not to fulfil the predicate
}
let evens = arr.filter(i=> i%2==0);
let odds = arr.filter(i=> i%2==1);
let result = evens.concat(odds);
I believe that's O(n). Have fun.
EDIT:
Or if you really care about efficiency:
let evens, odds = []
arr.forEach(i=> {
if(i%2==0) evens.push(i); else odds.push(i);
});
let result = evens.concat(odds);
Array.prototype.getEvenOdd= function (arr) {
var result = {even:[],odd:[]};
if(arr.length){
for(var i = 0; i < arr.length; i++){
if(arr[i] % 2 = 0)
result.odd.push(arr[i]);
else
result.even.push(arr[i]);
}
}
return result ;
};

Categories