JS - How to find the index of deepest pair of parentheses? - javascript

I have a string:
"5 * ((6 + 2) - 1)"
I need to find the deepest pair of parentheses and their contents.
I've googled a lot and I can't find anything specific to finding the index. A lot of solutions found how many levels there are, and things like that, but nothing was really helpful. I was thinking of counting the layers, and then using a loop to solve and repeat solving until done, but it seems like this would be really slow.
I have no idea where to start, so I haven't written any code yet.
I want a function to return 5, the string index of the deepest set of parentheses. I also need to do the same for the deepest ")", since I need the pair. Example:
const deepestPair = (str) => {
// Find deepest pair of parentheses
}
deepestPair("(2(5)4)(3)") // Returns [2, 4], the indexes of the deepest open/close parentheses

You could check opening and closing parentheses and use a counter for getting the most nested indices.
const deepestPair = str => {
var indices,
max = 0,
count = 0,
last;
[...str].forEach((c, i) => {
if (c === '(') {
last = i;
count++;
return;
}
if (c === ')') {
if (count > max) {
indices = [last, i];
max = count;
}
count--;
}
});
return indices;
}
console.log(deepestPair("(2(5)4)(3)")); // [2, 4]

You can use RegExp ([(])[^()]+[)] to match ( followed by one or more characters that are not ( or ) and closing ), /[)]/ to match closing parenthesis, return indexes of matches
const deepestPair = (str, index = null) =>
[index = str.match(/([(])[^()]+[)]/).index
, str.slice(index).match(/[)]/).index + index]
console.log(deepestPair("(2(5)4)(3)"));

Here is a simple way to get the deepest pair using two stacks. It also returns the depth of the pair in a structure, with the open and close indices.
It uses a singles stack to hold the open parenthesis found so far, and another stack (pairs) for matched parenthesis.
Each time a closing parenthesis is found, the last open parenthesis is popped from the singles stack and put in the pairs.
Then you just have to sort this pairs stack using the depth property and get the first item.
const deepestPair = str => {
const singles = [];
const pairs = [];
[...str].forEach((c, i) => {
if (c === '(') {
singles.push({ depth: singles.length + 1, open: i });
} else if (c === ')' && singles.length) {
pairs.push({ ...singles.pop(), close: i });
}
})
pairs.sort((a, b) => b.depth - a.depth);
return pairs.length ? pairs[0] : {};
};
console.log(deepestPair('(2(5)4)(3)'));
console.log(deepestPair('(2(5)(1)(11(14))4)(3)'));
If you want to get an array as the result you can replace the last line by this:
return pairs.length ? [pairs[0].open, pairs[0].close] : [];

Related

Find word in string using RegEx javascript

How can I make a search function using regEx?
I have some code but can't post it at the moment due to my computer being very broken and annoying, I will try again tomorrow!
This is not really what would be called combinations, but permutations.
The idea is to use recursion for getting the result for a shorter array, i.e. the one that lacks the first element.
Then take all permutations you get back from the recursive call, and insert the left-out value in each possible index.
When input has duplicates, then you need to stop inserting the left-out value as soon as you find that same value preceding the insertion spot.
Here is how that looks:
function scramble(array) {
if (array.length == 0) {
return [[]];
}
let results = [];
// solve the problem for a shorter array (first value excluded), and iterate:
for (let perm of scramble(array.slice(1))) {
// add the missing element in each possible position:
for (let i = 0; i < array.length; i++) {
// next IF is only needed when input has duplicates, and
// output should not repeat same array:
if (i && array[0] === perm[i-1]) break;
results.push(perm.slice(0, i).concat(array[0], perm.slice(i)));
}
}
return results;
}
let array = ["I", "am", "coding", "am"];
console.log(scramble(array));
Without the inner if, an input with duplicate values will produce duplicate arrays. If this is not desired, then the if is needed.
You could iterate and get a flat array of the mapping of the value with the result of the nested calls.
function permutation(array) {
return array.length === 1
? [array]
: array.flatMap((v, i) => permutation([
...array.slice(0, i),
...array.slice(i + 1)
]).map(a => [v, ...a]));
}
permutation(["I", "am", "coding"]).map(a => console.log(...a));

Recursive sort in JS

I was asked in an interview to write a program/algo to sort an array of number using recursion.
Though I vaguely answered it, I tried and came up with following code:
You can use following JSFiddle link to play around.
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([5,4,3,2,1]))
The algo I implemented is:
Take the array and check if its length is greater than 2.
If yes,
Remove last element and store it in a variable.
Again call same function without last element till it has 2 items.
Accept array returned from recursive call and peek the last element.
If newLast value is greater than previousLast
Push previousLast as first element and again call itself with this array.
If not, push previousLast to array and return it.
Else,
For number and string check equality and return correct order.
For anything else, return same value
Question is, is there a better way to implement (algo wise)?
Note: I'm not expecting code improvements. Objective of this question is improvement in algo part or any general stuff I have missed.
I also know, current code does not support:
Sort order. It will sort ascending only.
May break for Date objects, and does not support Objects in general.
Thanks!
I think most interviewers would expect you to respond with quicksort or merge sort (or both) given that question. Of the two, quicksort, is easier to remember and recreate in a pinch because the merge step of merge sort is easy to mess up.
Quicksort is a really beautiful algorithm and is a natural fit for javascript's functional tools. It is worth really understanding if you'll be doing interviews:
const arr = [6, 1, 5, 3, 9, 6, 7, 10, 16, 4, 0, 12, 2]
function qsort(arr){
if (arr.length < 2) return arr
// choose a pivot, p
// the choice of pivot can effect worst-case performance
// for this, we'll just use the first element.
const [p, ...rest] = arr
// partition array into element greater and lesser that the pivot
// this can be optimized so you don't loop through the array twice
const low = rest.filter(n => n <= p)
const high = rest.filter(n => n > p)
// recurse on both partitions and reassemble as recursion unwinds
return [...qsort(low), p, ...qsort(high)]
}
console.log(qsort(arr).join(', '))
I see a vein of intermediate value creation that is not inconsequential.
peekLast calls Array.prototype.slice which makes a copy of the array. You copy an entire array just to return the last element.
Array.prototype.peekLast = function () { return this.slice().pop(); }
Array.prototype.peekLast = function () { return this[this.length]; }
This gives you the same result every time without the need to copy.
Use of spread arguments in expressions like [ ...arr, x ] copies arr entirely.
arr.concat([ x ]) does the same thing without making copy (or mutation) of arr
You call peekLast and use ...x once per element in the input. Calling sort on a list of just 100 items will copy over 10,000 elements, for these operations alone. A list of just 1,000 items will copy over 1,000,000 elements. Room for algorithm improvment? For sure.
Mark Meyer starts you off on the right foot. If you're going to use recursion, it's best writing your program in functional style, as it will yield the best results. Mixing imperative style (statements, mutations, reassignments, other side effects, etc) with recursion is a recipe for a migraine.
Mark's algorithm, however great a "code improvement", your question is asking for "algorithm improvements". Under this lens, Mark's algorithm suffers from similar intermediate value creation by use of many ...x expressions.
Another lurking offense is the double use of .filter on the same array, rest. This creates an inefficient process as it iterates entirely through rest two (2) times per element. This is a symptom of reaching for low-hanging built-in functions that do close to what you want, but not exactly what you want. A better function would iterate through the array once and return both results.
The inefficiencies in Mark's program are mostly forgivable because of the dramatic improvement in code quality. His program is much more readable than yours because he's using functional style, which is where recursion comes from. The inefficiencies are also very easy to fix, so maybe that's an exercise for you?
Let's see if that gets your brain going. We'll see what answers other people submit before smothering you with too much information.
Your code will fail if we have duplicate elements because of this line.
if (newLast < last) {
It will go into infinite recursion
Refer the snippet with the duplicate array passed as input
function sort(arr) {
if (arr.length === 2) {
const v1 = arr[0];
const v2 = arr[1];
const isGreater = (
(isString(v1) && isString(v2) && v1.toString().toLocaleCompare(v2) > 0) ||
(isNumber(v1) && isNumber(v2) && v1 > v2)
);
return isGreater ? [ v2, v1 ] : [ v1, v2 ];
} else {
const last = arr.pop();
const ret = sort(arr);
const newLast = ret.peekLast();
debugger;
if (newLast < last) {
return [ ...ret, last ];
} else {
return sort( [ last, ...ret ] );
}
}
}
function isString(value) { return typeof value === 'string'; }
function isNumber(value) { return Number.isFinite(value); }
Array.prototype.peekLast = function () { return this.slice().pop(); }
//console.log(sort([1,2,3,4,5]))
console.log(sort([3,3,5,2]))
this one work for me to sort an array recursively:
var array = [3,1,8,2,4,9,16,28];
const sum = (arr, i=0)=> {
if(i === arr.length) return arr;
if(arr[i+1] < arr[i]){
const x = arr[i+1];
arr[i+1] = arr[i];
arr[i] = x;
}
return sum(arr,i+1);
}
console.log(sum(array))
function swap(arr, firstIndex, secondIndex){
let a= arr[firstIndex];
arr[firstIndex] = arr[secondIndex];
arr[secondIndex] = a;
return arr;
}
function sortArr(arr, index=0){
if(index == arr.length) return arr;
for(let i=0;i<arr.length; i++){
if(arr[i] > arr[i+1]){
arr = swap(arr, i, i+1);
}
}
return sortArr(arr, index+1);
}
console.log(sortArr([4,1,3,2,0]));
function quicksort(num){
if (num.length < 2){
return num
}
let pivot = num[0];
let slicedArr = num.slice(1);
let left = [];
let right = [];
for(let i = 0; i < slicedArr.length; i++){
if(slicedArr[i] <= pivot){
left.push(slicedArr[i])
}else{
right.push(slicedArr[i])
}
}
return [...quicksort(left), pivot, ...quicksort(right)]
}

Compare two arrays and create a new array with the differences, accounting for duplicates

Lets say I have two arrays containing numbers..
[1,1,7,6],
[1,7]
I need to create a new array out of the numbers that do not appear in the second array, but also account for there being two 1s.
I have tried using lodash's _.difference([1,1,7,6],[1,7])
as well as some other plain Javascript functions, but they do not account for the there being two 1s, so I just end up with [6]
My desired outcome would be [1,6]
Order does not matter
Iterate over the first array. For each element in the first array, search the second. If it exists, insert null into that index. If it doesn't exist, then push it to a new array with your differences
const difference = (arr1, arr2) => {
const differenceArr = []
for (let i = 0; i < arr1.length; i++) {
const matchingIdx = arr2.indexOf(arr1[i]);
if (matchingIdx !== -1) {
arr2[matchingIdx] = null
} else {
differenceArr.push(arr1[i])
}
}
return differenceArr
}
console.log(difference([1, 1, 7, 7, 6],[1, 1, 7]))
A shorter script:
function differenceWithDuplicates(a, b) {
while(b.length) a.splice(a.indexOf(b[0]), +a.includes(b.shift()));
return a;
}
console.log(differenceWithDuplicates([1, 1, 7, 6],[1, 7]));
Or just make the while, without call it inside a function.
You could do something along the lines of:
const diff = (array1, array2) => {
const _array2 = Array.from(array2);
const difference = [];
array1.forEach((array1Item) => {
const matchedIndex = _array2.findIndex(array2Item => array2Item === array1Item);
if (matchedIndex > -1) {
// Remove from second array
_array2.splice(matchedIndex, 1);
} else {
// No match found, add to difference array
difference.push(array1Item);
}
});
// Return the remaining items in _array2 and difference
return [ ..._array2, ...difference ];
}
By copying the second array, you can remove any matches from it. And by combining that with matching array1 > array2, you get differences in both directions, including duplicates.
Working fiddle: https://jsfiddle.net/3v34tjnq/

Why is my code pushing every permutation twice?

I'm confused as to why my code is pushing every permutation twice. Please someone help. I'm using heap's algorithm:
var regex = /(.)\1+/g;
function permAlone(str) {
var newArray = str.split('');
var n = newArray.length;
var permutations = [];
var tmp;
function swap(index1, index2) {
tmp = newArray[index1];
newArray[index1] = newArray[index2];
newArray[index2] = tmp;
}
function generate(n, newArray) {
if (n === 1) {
permutations.push(newArray.join(''));
} else {
for(var i = 0; i<n-1; i++) {
generate(n-1, newArray);
swap(n % 2 ? 0 : i, n-1);
permutations.push(newArray.join(''));
}
generate(n-1, newArray);
}
}
generate(n, newArray);
return permutations;
}
permAlone('aab');
The array that is returned is:
["aab", "aab", "aab", "baa", "baa", "aba", "aba", "aba", "baa", "baa"]
So as you can see, the permutations are appearing many more times than intended for each thing. Any help would be great
The code's a little complex and it's difficult to track given the recursion, but if all you want is an array with only unique values, you can simply apply the following code to the result array:
function stripDuplicates(input) {
if (!input || typeof(input) !== 'object' || !('length' in input)) {
throw new Error('input argument is not array.');
}
var newArray = [];
for (var i = 0; i < input.length; i++) {
if (newArray.indexOf(input[i]) === -1) {
newArray.push(input[i]);
}
}
return newArray;
}
This could also be done functionally rather than imperatively, but that's really more of a preference than an optimization issue.
Bálint also points out that you could merely convert the result to a Set, then convert the Set back to an Array, which would automatically strip out any duplicates. Beware, though, that Set is a comparatively new affordance in Javascript and will not function in pre-ES6 environments.
You have a call to:
permutations.push(newArray.join(''));
inside of your for loop. That shouldn't be there. And then, of course if you are permuting strings that have duplicate characters, well, expect to see dupes. e.g., if you permute the string "aa" you'll get two entries from this algorithm "aa" and "aa". Heap's algorithm doesn't try to remove dupes, it treats each element as unique within the string. Obviously, it's trivial to use remove dupes if that's something you care about doing.

Javascript Map Array Last Item

I have this:
map = ranks.map((row, r) => (
row.map((rank, i) => {
return [element(r, i, state, rank, toggled, onClick)];
})
));
It maps through a 2-dimentional array.
After each row, I'd like to insert <div class="clearfix"></div>.
I think, if I could somehow get the last index for each row, so I will be able to use it in the row map callback. Can someone show me how to do it?
Try something like:
row.map((rank, i, row) => {
if (i + 1 === row.length) {
// Last one.
} else {
// Not last one.
}
})
Old answer:
const rowLen = row.length;
row.map((rank, i) => {
if (rowLen === i + 1) {
// last one
} else {
// not last one
}
})
As LeoYuan answered, this is the correct answer, but it can be a bit improved.
map accepts a function with a third parameter, which is the iterated array itself.
row.map((rank, i, arr) => {
if (arr.length - 1 === i) {
// last one
} else {
// not last one
}
});
or in a bit shorter version, using an object destructuring (thanks Jose from the comments):
row.map((rank, i, {length}) => {
if (length - 1 === i) {
// last one
} else {
// not last one
}
});
Using an arr.length instead of row.length is a better and correct approach for several reasons:
When you mix scopes, it may lead for an unexpected bugs, especially in a poorly written or poorly designed code. In general, it is always a good way to avoid mixing between scopes when possible.
When you like to provide an explicit array, it will work as well. E.g.
[1,2,3,4].map((rank, i, arr) => {
if (arr.length - 1 === i) {
// last one
} else {
// not last one
}
});
If you like to move the callback outside of the map scope (mainly for a better performance), it will be wrong to use row.length as it is out of scope. E.g. in the OP case:
const mapElement = (rowIndex, state, toggled, onClick) => {
return (rank, i, arr) => {
let lastIndex = arr.length - 1;
return [element(rowIndex, i, state, rank, toggled, onClick, lastIndex)];
};
};
map = ranks.map((row, r) => row.map(mapElement(r, state, toggled, onClick)));
Fewer lines of code with the same results
row.map((rank, i, {length}) => (
//last element
if(i + 1 === length){
}
));
A slight improvement on the accepted answer:
const lastIndex = row.length - 1;
row.map((rank, i) => {
if (i === lastIndex) {
// last one
} else {
// not last one
}
})
This removes the arithmetic from inside the loop.
A shorter method would be to use .map combined with ternary operator, like this.
const positions = ["first", "second", "third", "fourth"]
positions.map((x, index, array) => {
index === array.length -1
? console.log("this is the last item in the array")
: console.log( x)
}
//////////// explanation
x ### returns the current element .map is looping through
index ### returns the index(location of item in an array) of the current element.
array ### return the same element we are looping through so if we use sth like this
["first", "second", "third", "fourth"].map...
we'll still get the array we're looping through
array.length - 1 ### gives us the length of the array and - 1 gives us the index of the last element in the array.
simplify answer above
const array = ['apple','orange','banana'];
array.map((element, index) => (index === array.length - 1) ? \`${element}.\` : \`${element},\`);
you can check last index with your array's length. here is a logic
var randomnumber = Math.floor(Math.random() * (100 - 10 + 1)) + 10
console.log("your last index is dynamic, ehich is ",randomnumber-1);
let arry = [];
for (i=1;i<randomnumber;i++){
arry.push(i)
}
arry.map((data,index)=>{
if(index == arry.length-1 ){
console.log("last index data ",data)
}
else{
console.log("remain data ",data)
}
})
console.log("your last index is dynamic, which is ",randomnumber-1);
this is also works in dynamic arry changes..
it is a too simple technique which i use .. :-)
const array = ['apple','orange','banana'];
array.map((element, index) => {
//Last element
if (index === array.length - 1) {
return `${element}.`;
}
//Another elements
return `${element}, `;
})}
Will return apple, orange, banana.
Perhaps the most concise way (although a little "dirty" – you can get some ESLint errors and TypeScript also might not be happy about that) to access the length property in array.map() is to pull it out (by destructuring) of the third callback argument (which is the array we are mapping over) and then assign a new property e. g. lastIndex, which value is being derived from that previously pulled out length:
let list = ["Alice", "Bob", "Cedrick", "David", "Emily"]
let mapped = list.map((item, i, {length, lastIndex = length - 1}) => {
return i === lastIndex ? "lastitem: " + item : item
})
console.log(mapped)

Categories

Resources