count duplicate value in recursion - javascript

how to count the duplicate values in string/array by recursion, well i know how to find the duplicate and unique by the loop with the object, but in this case, i tried to recursion it to learn my logic,
here is what i tried
function duplicate(word) {
let [obj, arr, count ] = [{},[],0]
for (i of word) {
obj[i] = (obj[i] || 0) + 1
if (obj[i] == 2) {
arr.push(i);
}
}
// console.log(arr);
let words = [...word];
words = [...new Set(words)];
// // return word
if (words.length === 0) {
return count
} else if (arr.includes(words[0])) {
count++
}
return count + duplicate(words.slice(1))
}
console.log(duplicate('xmikemk')) // 2
i tried to create object fist and pust to newArr if the values found 2 times,
then i tried to SET the word in again below to just show the unique value, then i recursions it, when i console log it, the slice method on that below to recursion and is not working, that is why the ouput i got here is 0, what is wrong with my code ? am I wrong with that condition??
that variable word has = ('xmikemk') which has 2 duplicate words, m and k , so the output i want is2`

You could spread the given string and take only the first character for counting, by taking another parameter for handing over the last counted values.
If no more spreadable items, the count is made and returned. Otherwise go on with the recursive call.
function duplicate([w, ...rest], counter = {}) {
counter[w] = (counter[w] || 0) + 1;
if (!rest.length) {
return Object.values(counter).reduce((s, c) => s + (c > 1), 0);
}
return duplicate(rest, counter);
}
console.log(duplicate('xmikemk')) // 2

You are making this more complicated than it is. Just create an internal accumulator method to populate a map and increment the frequency.
function duplicate(word) {
return duplicateInternal(word, {});
}
function duplicateInternal(word, mapping) {
if (word == null || word.length === 0) {
return mapping;
}
var c = word.charAt(0);
mapping[c] = (mapping[c] || 0) + 1;
return duplicateInternal(word.substr(1), mapping);
}
var mapping = duplicate('xmikemk');
var frequency = Object.keys(mapping)
.map(key => [key, mapping[key]])
.sort((a, b) => {
let diff = -1 * (a[1] - b[1]);
return diff === 0 ? a[0].localeCompare(b[0]) : diff;
});
console.log(frequency.map(x => x.join(' x')).join('\n'))
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}

Related

Codwars: Array to single value +1

Given an array of integers of any length, return an array that has 1 added to the value represented by the array.
the array can't be empty
only non-negative, single digit integers are allowed
Return nil (or your language's equivalent) for invalid inputs.
Examples
For example the array [2, 3, 9] equals 239, adding one would return the array [2, 4, 0].
My code so far:
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return arr.some(e => typeof e !== 'number' || e < 0) ?
null : i
.toString()
.split('')
.map(e => parseInt(e));
};
It seems to pass most basic test however fails with larger inputs. Where have I gone wrong?
Just like you have converted the array into a number, you have to convert the number back into an array.
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return i.toString().split('').map(x => parseInt(x));
};
console.log(upArray([2,3,9]));
Your code won't work if the array length is greater than 100k...
Number type of javascript or any language is not enough big to handle it.
It's better if we calculate the last element with 1. If result is larger than nice ( < 10 ),
we continue to calculate next element with 1 and assign current value to 0. If result is smaller or equal 9, just assign the result to current and exit loop.
Then we print the final array as result:
pseudo code:
for i from: n-1:0
result = arr[i] + 1;
if(result < 10) :
arr[i] = result;
exit loop;// no need to continue calculate
else:
arr[i] = 0;
endif;
endfor;
You can join final array as string.
Here's probably the fastest solution (performance wise) - also there's no need to deal with BigInt, NaN, or Infinity:
function upArray(arr) {
if (!isInputIsNonEmptyArray(arr)) {
return null;
}
const isNumber = num => typeof num === 'number';
const isIntSingleDigit = num => Number.isInteger(num) && num >= 0 && num <10;
let resultArr = [];
let i = arr.length;
let num;
while (i-- > 0) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
if (num === 9) {
resultArr[i] = 0;
if (i === 0) { //means we're in the msb/left most digit, so we need to insert 1 to the left
resultArr.unshift(1);
break; //you can leave it out really, as the next check in the while will fail anyway
}
}
else {
resultArr[i] = num + 1; //No more + 1 should be made, just check for validity
//of the rest of the input and copy to the result arr
while (--i > -1) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
resultArr[i] = arr[i];
}
break;
}
}
return resultArr;
function isInputIsNonEmptyArray(arr) {
return Array.isArray(arr) && arr.length > 0;
}
}
If the input arg is not an array or an empty array, or if you encounter invalid element during the main while loop you return null.
In the main while loop you go from the right most element (lsd), and add 1 to it (or insert 0 if the number is 9) up the the left most digit.
If a number which is less than 9 is incremented, no need to increment any more (this is the while loop in the else clause).

Getting time and space complexity of a JS function

Here' i have a solution of a problem of removing consecutive duplicate characters. But i need to know the time and space complexity of this solution.
Here's the function:
function removeAdjacentDuplicates(str){
for (let i = 0; i< str.length -1; i++){
if(str[i] === str[i+1] && i+2==str.length){
return str.substr(0,i);
} else if(i+2==str.length ||str=="" || str.length==0){
return str;
} else if (str[i] === str[i+1]){
return removeAdjacentDuplicates(str.substr(0,i)+str.substr(i+2,(str.length)-(i+2)));
}
}
};
//removeAdjacentDuplicates('abbc') returns 'ac'
I'm guessing it should be O(n) for both because for each input the function needs to loop over the whole. Also suggestion for making the function better would be appreciated too.
I suppose a simple reduce will be sufficient and not too complex here.
How to determine complexity is explained nicely here. Furthermore, there are plenty of articles to be found on the subject (e.g. here or here).
See also
console.log(`original: abba and,, sommmme of thaat ook??`, ` `);
console.log(`removeAdjacentDuplicates: ${
removeAdjacentDuplicates('abba and,, sommmme of thaat ook??')}`);
console.log(`removeDuplicateCharacterPairs: ${
removeDuplicateCharacterPairs('abba and,, sommmme of thaat ook??')}`);
console.log(`RemoveConcurrentCharacters: ${
RemoveConcurrentCharacters([...'abba and,, sommmme of thaat ook??'])}`);
console.log(`RemoveConcurrentCharactersReducer: ${
RemoveConcurrentCharactersReducer([...'abba and,, sommmme of thaat ook??'])}`);
// this removes one of duplicate character pairs
function removeAdjacentDuplicates(str) {
return str.split('')
.reduce((acc, val) =>
val === acc.slice(-1) ? acc : acc + val, '');
}
// this removes actual duplicate pairs,
// but the result may contain new duplicates
function removeDuplicateCharacterPairs(str, result = []) {
str = str.split("");
const first = str.shift();
str.length && first !== str[0] && result.push(first) || str.shift();
return str.length ?
removeDuplicateCharacterPairs(str.join(""), result) :
result.join("");
}
// this removes duplicate pairs. When the result
// contains new duplicate pairs removes them too
// so the result will not contain any duplicate
// character pair
function RemoveConcurrentCharacters(arr) {
let result = [];
for (let i = 0; i < arr.length; i += 1) {
const len = result.length - 1;
i < 1 && result.push(arr[i]);
if (i > 0) {
arr[i] !== result[len] &&
result.push(arr[i]) ||
result.splice(len, 1);
}
}
return result.join("");
};
// making a round trip: RemoveConcurrentCharacters can be
// condensed using a reducer method.
function RemoveConcurrentCharactersReducer(arr) {
return arr.reduce((acc, val) =>
!acc.length ? [val] : val === acc[acc.length-1] ?
acc.slice(0, -1) : [...acc, val], [])
.join("");
};

Rearranging a string to be a palindrome

I'm trying to solve the problem of: Given an array of strings with only lower case letters, make a function that returns an array of those same strings, but each string has its letters rearranged such that it becomes a palindrome (if not possible then return -1). I'm a bit stuck on how I should be rearranging the letters.
let arr = ["hello", "racecra"];
I created a function to first check if a word is a palindrome :
function isPalindrome(arr) {
let obj = {};
for (var x = 0; x < str.length; x++) {
if (obj[arr[x]]) {
obj[arr[x]] += 1;
} else {
obj[arr[x]] = 1;
}
}
let countOdd = 0;
let countEven = 0;
for (let x of Object.values(obj)) {
if (x % 2 == 0) {
countEven += 1;
} else {
countOdd += 1;
}
}
return countOdd == 1 ? true : false
}
then I plan to loop through the words
let emptyArr = [];
for (var x = 0; x < arr.length; x++) {
if (isPalindrome(arr[x]) {
// not sure what to do here. I know the word is a palindrome but not sure how to sort the order of the word in the palindrome form.
} else {
emptyArr.push(-1);
}
}
return emptyArr;
Look closely: you don't need your words to be palindromes, you need them to be rearrangeable as palindromes ("palindrome-candidates"). Now, a word is a palindrome-candidate if all of its letters but one can be counted by an even number (2, 4, 6 etc.)
For example, this...
hollo
... is NOT a palindrome, but can become one, as there's 2 'o', 2 'l' and just one 'h' in it. To rearrange, you just move 'h' in the middle, then just place 'o' and 'l' before and after it:
l -> o -> h <- o <- l
So start with splitting each of your words by characters, then either count those characters or just sort them (as #Barmar suggested). If they satisfy the condition, rearrange the letters following the approach given; if not, return null (or any other special value clearly distinguishable from the rest) immediately.
Here's one way to do it:
function rearrangeAsPalindrome(word) {
if (word.length === 1) return word; // easy win first
const charCounter = word.split('').reduce((counter, ch) => ({
...counter,
[ch]: (counter[ch] || 0) + 1
}), {});
const parts = ['', '', '']; // left, middle, right
const entries = Object.entries(charCounter);
for (let i = 0; i < entries.length; ++i) {
const [char, counter] = entries[i];
if (counter % 2) { // odd
if (parts[1] !== '') return null;
// one odd is already here, eject! eject!
parts[1] = char.repeat(counter);
}
else { // even
const half = counter / 2;
parts[0] = char.repeat(half) + parts[0];
parts[2] += char.repeat(half);
}
}
return parts.join('');
}
console.log(rearrangeAsPalindrome('racarrrac')); // crraaarrc
console.log(rearrangeAsPalindrome('aabbcc')); // cbaabc
console.log(rearrangeAsPalindrome('hollo')); // lohol
console.log(rearrangeAsPalindrome('hello')); // null
This function returns null (and does it early) when it realizes the word given cannot be rearranged as a palindrome - or an actual palindrome if it is possible.
This can help
"How to generate distinct palindromes from a string in JavaScript"
https://medium.com/#bibinjaimon/how-to-generate-distinct-palindromes-from-a-string-in-javascript-6763940f5138

Return the first non-repeating character of a string

In the first chunk of my code I have an ' if ' statement that is not working as it should, and I can't figure out why.
When using the argument 'hous', it should enter the first ' if ' statement and return 0. It returns -1 instead.
var firstUniqChar = function(s) {
for (let i = 0; i < s.length; i++){
let letter = s[i];
// console.log('s[i]: ' + letter);
// console.log(s.slice(1));
// console.log( 'i: ' + i);
if ((i = 0) && !(s.slice(1).includes(letter))) {
return 0;
}
if ((i = s.length - 1) && !(s.slice(0, i).includes(letter))) {
return 1;
}
if(!(s.slice(0, i).includes(letter)) && !(s.slice(i + 1).includes(letter))) {
return 2;
}
}
return -1;
};
console.log(firstUniqChar("hous"));
This is another way you can write your function:
const firstUniqChar = s => [...s].filter(c=>!(s.split(c).length-2))[0] || -1;
console.log(firstUniqChar("hous"));
console.log(firstUniqChar("hhoous"));
console.log(firstUniqChar("hhoouuss"));
Look up method for scattered repeated characters and functional find()-based approach
You may break your input string into array of characters (e.g. using spread syntax ...) and make use of Array.prototype.find() (to get character itserlf) or Array.prototype.findIndex() (to get non repeating character position) by finding the character that is different from its neighbors:
const src = 'hhoous',
getFirstNonRepeating = str =>
[...str].find((c,i,s) =>
(!i && c != s[i+1]) ||
(c != s[i-1] && (!s[i+1] || c != s[i+1])))
console.log(getFirstNonRepeating(src))
.as-console-wrapper{min-height:100%;}
Above will work perfectly when your repeating characters are groupped together, if not, I may recommend to do 2-passes over the array of characters - one, to count ocurrence of each character, and one more, to find out the first unique:
const src = 'hohuso',
getFirstUnique = str => {
const hashMap = [...str].reduce((r,c,i) =>
(r[c]=r[c]||{position:i, count:0}, r[c].count++, r), {})
return Object
.entries(hashMap)
.reduce((r,[char,{position,count}]) =>
((count == 1 && (!r.char || position < r.position)) &&
(r = {char, position}),
r), {})
}
console.log(getFirstUnique(src))
.as-console-wrapper{min-height:100%;}
function nonRepeat(str) {
return Array
.from(str)
.find((char) => str.match(newRegExp(char,'g')).length === 1);
}
console.log(nonRepeat('abacddbec')); // e
console.log(nonRepeat('1691992933')); // 6
console.log(nonRepeat('thhinkninw')); // t

Javascript | Dynamic array of Characters based on consecutive letters conditions

I was doing a codewar challange, and couldn't find a solution, but I really want to know how we can solve this problem.
So we getting two integers, let's say N and D and we should return a string containing exactly N letters 'n' and exactlly D letters d with no three consecutive letters being same.
For example if we get N=5 and D=3 we should return "nndnndnd" or "nbnnbbnn" or any other correct answer
another example like if we get N=1 D=4 the only accepted answer should be "ddndd"
What I did was making a helper function like this :
function generateArray (char,q){
let arr= []
for(let i=0; i<q; i++){
arr.push(char)
}
return arr
}
and inside the main function :
function solution(N, D) {
let arrayOfchar = generateArray('n',N)
arrayOfchar.reduce((prev,current,index) => {
for(let i=0; i<D; i++) {
if(prev===current) {
arrayOfchar.splice(index, 0, "d")
}
}
})
}
But I don't know hoe should I put the "d" only after two or less consecutive "n"
Anyone clue?
Rather than creating an entire array of the same character at the very start, I think it would make more sense to create the array piece-by-piece, until N and D come out to 0.
Here's one possible implementation. The general idea is to try to push whichever character count is larger, or if that's not possible due to 3-in-a-row, push the other character, and subtract the appropriate character count by one. Repeat until both counts are 0:
function solution(n, d) {
const arr = [];
function canPush(char) {
const { length } = arr;
return (arr[length - 1] !== char || arr[length - 2] !== char);
}
function push(char) {
arr.push(char);
if (char === 'n') n--;
else if (char === 'd') d--;
}
while (n > 0 || d > 0) {
if (n > d) {
if (canPush('n')) push('n');
else if (d === 0) return console.log('Impossible');
else push('d');
} else if (d >= n) {
if (canPush('d')) push('d');
else if (n === 0) return console.log('Impossible');
else push('n');
}
}
console.log(JSON.stringify(arr));
// return arr;
}
solution(5, 3);
solution(1, 4);
solution(1, 5);
solution(5, 1);
solution(2, 5);
solution(2, 6);
solution(2, 7);
Here is another solution to this interesting problem. The idea is not to go one by one but to figure which one is the larger number and then do an array of pairs of that letter while doing a simple array of the smaller and then just concat them one with another ... so you have 5 and 3 ... nn + d + nn + d + n. 2 pairs of the bigger plus one of the smaller etc.
const fillArray = (length, letter, bigNumber) => {
var arr = []
for(var index=0; index < length; index++) {
arr.push([letter, bigNumber%2 && index+1 === length ? null : letter])
}
return arr;
}
const getString = (n, d) => {
var swtch = d > n, arr = [],
bigger = {number: swtch ? d : n, letter: swtch ? 'd' : 'n', ceil: Math.ceil((swtch ? d : n)/2)},
smaller = {number: swtch ? n : d, letter: swtch ? 'n' : 'd', ceil: Math.ceil((swtch ? n : d)/2)}
if(Math.abs((bigger.number/2) - smaller.number >= 1.5)) {
return 'Not possible with given parameters!'
}
var bigWorkArray = fillArray(bigger.ceil, bigger.letter, bigger.number)
var smallWorkArray = n === d ? fillArray(smaller.ceil, smaller.letter, smaller.number) : Array(smaller.number).fill(smaller.letter)
for(var i=0; i < bigWorkArray.length; i++) {
arr.push(...bigWorkArray[i],...smallWorkArray[i] || '')
}
return arr.join('');
}
console.log(getString(5,3))
console.log(getString(1,4))
console.log(getString(1,5))
console.log(getString(5,1))
console.log(getString(2,5))
console.log(getString(2,6))
console.log(getString(2,7))

Categories

Resources