Unusual characters when defining where to replace / alter string - javascript

I have to see if the array given (arr) is in ascending order. (only positive integers given)
I created an array for something to compare it against. Then looped through the input arr to see if each iteration matches. Or at least that was my goal.
Have also tried a counter variable which adds one to the count only every time sorted[i] == arr[i] is true. Then if the count is the same as sorted.length it is true. However, the fact this didn't work made me think I have made a more fundamental error somewhere.
function inAscOrder(arr) {
let sorted = arr.sort((a, b) => a - b);
for (let i = 0; i < arr.length; i++) {
if (arr[i] === sorted[i]) {
return true;
} else {
return false;
}
}
}

sort modifies array in place, so you're comparing two identical arrays. You need to use .slice() on the array first to create a copy.
Also, you need to move return true to the end of the function, otherwise you will return after first match.

function inAscOrder(arr) {
return JSON.stringify(arr) === JSON.stringify(arr.concat().sort()));
}

You don't need to sort your array to check if it's sorted. Loop over each consecutive pair of elements and check if the first is less than the second; if you find a pair for which this isn't true, the array is not sorted.
function inAscOrder(arr) {
for (let i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i+1]) {
return false;
}
}
return true;
}

Related

Function take in array of string return only strings with that have number(s) in them. Or return empty array if none

The function has 1 parameter: an array. It should iterate through the array and return strings that contain a number in them. If none of them have an number it will return an empty array.
The code I have written so far seems too verbose. Sometimes it does not return the correct value. I am looking for any ways to shorten the code or improve it.
function numInStr(arr) {
var stringsWithNum = [];
for(var x = 0; x < arr.length - 1; x++) {
for (var y = 0; y < arr[x].length - 1;y++) {
//console.log(typeof arr[x][y]);
if('0123456789'.indexOf(arr[x][y]) !== -1) {
stringsWithNum.push(arr[x]);
break;
}
}
}
return stringsWithNum;
}
You can shorten your code considerably by using .filter() to filter out the elements containing numbers, and using .match() to test whether the elements contain a number.
This eliminates the need to create and maintain (and possibly incorrectly set) array indexes.
function numInStr(arr) {
return arr.filter(function (elmt) {
return elmt.match(/\d/)
})
}
console.log(numInStr(['foo', 'ab12', '34asdf', 'bar']))
// Array [ "ab12", "34asdf" ]

Have integer added to end of array if larger than rest of integers in array

My function goes through an array to find the lowest index it should be inserted into. I am assuming the array is always sorted. It works unless the integer is larger than the rest of the array integers. At this point, it needs to be added to the end of the array.
I tried to use if else statements and have the number appended with a push but it just goes into a never-ending loop since I will always stay less than arr.length. I tried adding a break after the push inside the else but then, it always appends without inserting into the correct position if there is a place inside the array for it already.
function lowestIndexInsert(num,arr){
for (i = 0; i<arr.length; i++){
if (arr[i]>num){
arr[i]=num;
}
else {
arr.push(num);
break;
}
}
return arr.indexOf(num);
}
lowestIndexInsert(15,[8,25,33,52,70]);// should return 1
lowestIndexInsert(80,[8,25,33,52,70]);// should return 5
You can use splice to insert an element to the array and then instantly break. Once this is done you can catch the final case where i = length and hasn't been inserted yet. If you use 3 arguments such as: .splice(start, deleteCount, insertMe) the function will insert the item at the specific index and delete none. With this you can do:
function lowestIndexInsert(num,arr){
for (i = 0; i<arr.length; i++){
if (arr[i]>num){
arr.splice(i, 0, num);
// Break here to stop the loop after inserting
break;
}
// Perform a final check to see if the item was inserted.
if(i == (arr.length - 1)){
arr.push(num);
}
}
return arr.indexOf(num);
}
lowestIndexInsert(15,[8,25,33,52,70]);// should return 2
lowestIndexInsert(80,[8,25,33,52,70]);// should return 5
If you want to iterate as many times as the original array was long, you have to remember what that length was. That's what variable are for.
Also, you don't know if you need to add to the array until you have finished searching it.
You're pushing inside the loop, so you'll get your new value pushed once for every single number in the array that's greater than the value. That's not what you want.
You can greatly simplify this code, and avoid manual loops altogether:
function lowestIndexInsert(num,arr) {
let index = arr.length;
arr.some((value, i) => {
if (value > num) {
index = i;
return true;
}
return false;
});
arr[index] = num;
}

Recursion to find n numbers in a range that add up to a target value

I've written a function that finds all sets of two numbers that sum a target value, given a range of numbers from 0 to x. I'm trying to rewrite it in a way so that you can get a result of a given length n (not only 2), but n numbers that will equal a target when added together.
For example, if the range is 1 to 5 and you want to find all sets of three numbers that add up to 7, the answer would be:
[[1,1,5], [1,2,4], [1,3,3], [2,2,3]]
It looks like the solution is to use a recursive function, but I can't quite figure out how to do this. I've looked at several subset-sum examples on StackOverflow, but none seem to match this particular scenario.
This is not a homework problem. Any help would be appreciated. Here is my findPairs function:
function findPairs(arr, target) {
var pairs = [];
var first = 0;
var last = arr.length-1;
while (first <= last) {
var sum = arr[first] + arr[last];
if (sum === target) {
pairs.push([arr[first], arr[last]]);
first ++;
last--;
}
else if (sum < target) {
first++;
}
else {
last--;
}
}
return pairs;
}
var sample = _.range(11);
console.log(JSON.stringify(findPairs(sample,12)));
// Returns
// [[2,10],[3,9],[4,8],[5,7],[6,6]]
This example uses the lodash _.range function. Fiddle here: https://jsfiddle.net/tbarmann/muoms1vL/10/
You could indeed use recursion. Define the third argument as the length of the sub-arrays you are looking for, and define a recursion function inside that function as follows:
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
var result = findSums([1,2,3,4,5], 7, 3);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Remarks
The solution does not require that the input array has consecutive numbers (range): you might pass it [3,6,4,2,1]. The sub-arrays that are returned will keep the selected elements in order, for example: [3,3,3], [3,4,2] and [6,2,1] could be solutions for targetting 9 with 3 values for that example input.
The numbers must all be non-negative. If negative values are to be allowed, then the optimisation if (leftOver < 0) return; must be removed from the code.
Well, what you are probably looking for is Dynamic Programming. It is an approach where you define mathematically your problem and a recursive solution. Then you try to find a solution, using memoization.
I would make a helper function, where the arguments are (range, target, lengthOfResult). Then do something like:
func helper(arr, target, lengthOfResult){
if(taget == 0) continue;
for(int i=1; i<arr.length-1; i++){
if(taget - arr[i] < 0) return [];
var sub_results = helper(arr, taget - arr[i], lengthOfResult - 1)
foreach(var res in sub_results){
result.concat([arr[i]] + res);
}
}
return result;
}
So you are changing the question for the helper function to "Give me all lists of length-1 which sums up to taget-arr[i]", append to that arr[i]. Then use that to construct the result, for each arr[i].
Basically, every iteration the length will decrease, so the function will terminate at some point. You subtract from the taget whatever number you have now. So you're end result will add up to the desired target.
Note that this algorithm works only with positive numbers. If you can allow negatives, you should remove the if inside the first for-loop.
About the memoization, if you want to allow using each number only once, you could get away with a single array. If you allow reoccurring numbers in the result (like in your example), you probably need a grid; horizontally the target, vertically the lengthOfResult. Then in the beginning of each invocation of the helper method, you check if you already calculated that value. This will save you some recursive calls and make the algorithm not exponential.
function sumPermutations(target, range, number) {
var result = [];
function combo(left, group, sum) {
if(sum > target) return null;
if (left == 0) {
if(sum == target) return group;
return null;
}
for (var i = range.min; i <= range.max; i++) {
var r = combo(left - 1, group.concat(i), sum + i);
if (r)
result.push(r);
}
}
combo(number, [], 0);
return result;
}
console.log(sumPermutations(7, {min: 1, max: 5}, 3));
Note: This gives results with duplicates (all permutaions including those with different orders). You can remove duplicates by sorting the arrays and join thier items and hash them into a hash object.

How to remove both instances of duplicated objects in an array

I have an array of objects. I am trying to find duplicated objects and then remove both instances of that object.
Right now I am using this method:
function checkForDups(data){
for(var i = 0; i < data.length; i++){
for(var j = i+1; j < data.length; j++){
if(data[j].number === data[i].number){
data.splice(j,1);
data.splice(i,1);
}
}
}
return data;
}
I believe problem is that it only checks for duplicates that have an index that greater than the current position it is checking. This means objects that are "behind" it in the array are not checked for duplication. Currently I run the array through this function a few times to get the desired results. However, this is obviously extremely inefficient. How could I achieve my desired results more efficiently?
I believe problem is that it only checks for duplicates that have an index that greater than the current position it is checking. This means objects that are "behind" it in the array are not checked for duplication.
No, that's a simply optimisation, made possible by the symmetry of the equality relation. By searching ahead and removing all duplicates in front of you, any current item can't be a duplicate of a previous one or it would have been already eliminated.
However, there are some things you did not take care of:
When splicing (removing) an item from the array, all subsequent ones are moved, and the array changes its length. To really examine all items in the array, you need decrease (or: not increase) the counter variable when removing, so that the new item which is now in the same spot as the removed one gets visited (the spot from which you just removed needs to get revisited).
You probably want to break the inner loop after having found a duplicate, otherwise you compare and remove totally different items.
You have not made clear what the algorithm should do when there are more than 2 duplicate items of the same sort in the array. Leave one when their number is odd? Thanks for your comment.
To remove all existing duplicates, you will need to continue the search, but must not remove the ith element immediately or you won't have anything to compare to furtheron - or you might even remove the ith item multiple times (see #2).
So this modification should fit:
function removeAllDups(data) {
// leaves only items in the array that appeared a single time
// removes everything whose .number can be found multiple times
for (var i = 0; i < data.length; i++) {
var found = false,
num = data[i].number;
for (var j = i+1; j < data.length; j++) {
if (data[j].number === num) {
found = true;
data.splice(j--, 1);
}
}
if (found) {
data.splice(i--, 1);
}
}
return data;
}
Here's an alternate implementation. This uses only two passes through the array, and removes all dupes in cases > 2: http://jsfiddle.net/nrabinowitz/1pdr780j/
function removeDupes(arr, test) {
test = test || function(a, b) { return a === b; };
function find(cache, element) {
return cache.some(test.bind(null, element));
}
var seen = [];
var dupes = [];
var len = arr.length;
var x;
var current;
// First pass - find dupes
for (x = 0; x < len; x++) {
current = arr[x];
if (find(seen, current)) {
dupes.push(current);
} else {
seen.push(current);
}
}
// Second pass: remove dupes. Reverse iteration saves headaches here
for (x = len - 1; x >= 0; x--) {
current = arr[x];
if (find(dupes, current)) {
arr.splice(x, 1);
}
}
}
This is an updated version that takes an optional test function to determine equality for dupe purposes; for the OP's case, the call would be
removeDupes(arr, function(a, b) {
return a.number == b.number;
});
Note that this assumes support for ES5 methods - Array#some, Function#bind. If you need to support older browsers, an ES5 shim or the Underscore library would fit the bill.
You could use underscore.js:
function checkForDups(data) {
return (
_.map(
_.filter(
_.pairs(
_.countBy(data)
), function(v) {return v[1] == 1}
), function(v) {return ~~v[0]}
)
)
}
Example
>> console.log(checkForDups([0,1,2,3,0,1,0,4]))
[2, 3, 4]
You can try it here.

Combination of a letter using recursive

Suppose, if I give 'ABC' as input then then I want 'ABC', 'ACB', 'CAB', 'CBA', 'BAC', 'BCA'. Each word has combination of n! where n is length of letter. I think recursion can make it easier. Here is my code written in javascript :
function reArrange(word)
{
console.log(word);
if (word.length < 0) {
return (-1);
}
else if (word.length == 0) {
return ('');
}
else {
for (var _i = 0; _i < word.length; _i++) {
var temp = word[_i];
for (var _j = 0; _j < word.length; _j++) {
if (_i != _j) {
return word[_i] + reArrange(word.slice(_i, word.length));
}
}
}
}
}
Please use detailed comment.
function combinations(current_string, actual_string, seen) {
var result = [];
if (current_string.length === actual_string.length) {
return [current_string];
}
actual_string.forEach(function(currentChar, index) {
if (seen.indexOf(index) === -1) {
result = [].concat.apply(result, combinations(current_string
+ currentChar, actual_string, seen.concat(index)));
}
});
return result;
}
console.log(combinations("", "ABC".split(""), []));
Output
[ 'ABC', 'ACB', 'BAC', 'BCA', 'CAB', 'CBA' ]
Note: This program works under the assumption that the characters in input string will be unique.
There are three parameters passed to this function. First one is the current string which was built with recursion, second is the array of characters from the actual string, third one is the list of indices already seen in the recursion tree.
The first if condition is the base condition of this recursive solution. If the length of the current string generated is equal to the actual string's length, we have no characters left to process and this is one of the combinations. So, we return that.
If that condition is not met, for each character in actual string, we check if it has been used already (we compare the indices with the indices in the seen). If it is already used in the current recursion, ignore that. Otherwise, concate that with the current string and include this in the seen variable and recurse now.
The result of the recursion will be an array of strings. We need to flatten them (concatenate all the elements of the inner arrays). So, we use [].concat.apply.
Finally, we return the gathered result and here is how the recursion tree looks like

Categories

Resources