JS How to for palindrome [duplicate] - javascript

This question already has answers here:
Palindrome check in Javascript
(45 answers)
Closed 4 years ago.
The question I have been given is this;
create a function that takes an array of words and returns an array containing only the palindromes.
A palindrome is a word that is spelled the same way backwards.
E.g. ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'] => ['racecar', 'pineenip']
This is the code that I create;
let arr = []
let str = words.slice(0)
let pal = str.toString().split("").reverse().join("")
console.log(pal);
for (let i = 0; i < words.length; i++) {
for (let k = 0; k < pal.length; k++) {
if (words[i] == pal[k]) {
arr.push(words[i])
}
}
}
return arr
}
This is the test that my code is run against;
describe("findPalindromes", () => {
it("returns [] when passed []", () => {
expect(findPalindromes([])).to.eql([]);
});
it("identifies a palindrom", () => {
expect(findPalindromes(["racecar"])).to.eql(["racecar"]);
});
it("ignores non-palindromes", () => {
expect(findPalindromes(["pineapple", "racecar", "pony"])).to.eql([
"racecar"
]);
});
it("returns [] when passed no palindromes", () => {
expect(findPalindromes(["pineapple", "watermelon", "pony"])).to.eql([]);
});
});
Does anyone have any any suggestion of how to make my code work?

This is the simplest function that returns true or false if the str is a palindrome or not.
I would use this in combination with the filter function to filter on all palindromes. Like this
function checkPalindrom(str) { //function that checks if palindrome or not
return str == str.split('').reverse().join('');
}
const result = words.filter(word => checkPalindrom(word)); //filter function that filters array to only keep palindromes

Without giving spoilers to the answer (this is a common interview question) a clean approach would be as follows:
Define a function isPalindrome(string): boolean
Use the filter property available on the Array prototype to return an array of only palindromes e.g. inputArray.filter(isPalindrome)
Both can be unit tested separately, for example:
You could define an array of inputs and expected outputs for isPalindrome [{ input: "racecar", expectedOutput: true}, {input: "pineapple", expectedOutput: false}, ...] and loop over each test case.

function isPalindrome(word) {
const firstHalf = word.slice(0, Math.ceil(word.length/2));
const secondHalfReversed = word.slice(Math.floor(word.length/2)).split('').reverse().join('');
return firstHalf === secondHalfReversed;
}
function getPalindromesFromArray(arr) {
return arr.filter(isPalindrome);
}
const wordsArr = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'];
console.log(getPalindromesFromArray(wordsArr));

using for loop and filter
let arr = ["foo", "racecar", "pineapple", "porcupine", "pineenip",'pap','aaaa'];
let palindromes = arr.filter(w => {
let len = w.length;
for (let i = 0; i < len / 2; i++) {
if (w[i] == w[len - i - 1]) {
return true;
} else {
return false;
}
}
});
console.log(palindromes)

To solve that first I would create an isPalindrome function like this:
function isPalindrome(word) {
palindromeWord = ''
for(var i = word.length - 1; i >= 0; i--) {
palindromeWord += word.charAt(i)
}
return palindromeWord === word
}
and then I would check for each word inside the array like this:
let arr = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip']
let palindromeArr = []
arr.forEach(word => {
if (isPalindrome(word)) {
palindromeArr.push(word)
}
})
console.log(palindromeArr)

What you have is good, however when you did
var pal = str.toString().split("").reverse().join("")
You changed from an array to a string, then you went into the loop with the string, so pal[k] gave a character and not a word.
To change pal back to an array of strings, split it again, use
var pal = str.toString().split("").reverse().join("").split(",");
var words = ['foo', 'racecar', 'pineapple', 'porcupine', 'pineenip'];
var arr = [];
var str = words.slice(0);
var pal = str.toString().split("").reverse().join("").split(",");
console.log(pal);
for (let i = 0; i < words.length; i++) {
for (let k = 0; k < pal.length; k++) {
if (words[i] == pal[k]) {
arr.push(words[i])
}
}
}
console.log(arr);

Related

Find indices within a string where any combination of an array of words is found

Sample data:
String: "barfoofoobarthefoobarman"
Array of words: ["bar", "foo", "the"]
Output:
[6, 9, 12]
I was asked this question during an interview. Due to time constraint, I tried to find all the possible words that could be made out of the array of words (i. e. "barfoothe"), but was told that would not scale for large arrays. Was suggested to use a map data structure, but I think my solution doesn't scale either, and it's brute forced.
Here's the solution.
var solution = function(string, words) {
let output = [];
let wordsMap = new Map();
let wordsNumber = words.length;
let wordLength = words[0].length;
words.forEach((word) => {
if (!wordsMap.has(word))
wordsMap.set(word, 1);
else
wordsMap.set(word, wordsMap.get(word) + 1);
});
for (let i = 0; i <= string.length-(wordsNumber*wordLength); i+=wordLength) {
let tempMap = new Map(wordsMap);
let check = true;
let tempString = string.substring(i, i + wordsNumber*wordLength);
for (let j = 0; j <= tempString.length - wordLength; j += wordLength) {
let tempString2 = tempString.substring(j, j + wordLength);
if (tempMap.has(tempString2))
tempMap.set(tempString2, tempMap.get(tempString2) - 1);
}
for (let val of tempMap.values()){
if (val !== 0){
check = false
break;
}
}
if (check)
output.push(i)
}
console.log(output);
}
solution("barfoothefoobarman", ["foo", "bar"]);
Any suggestion for a smarter solution?
You could create a dynamic regular expression.
const words = ['foo', 'bar']
const rx = new RegExp(words.join('|'), 'g')
// todo escape special characters
Then search away.
const counts = words.map(it=>0) // [0,0]
// todo use map or object to track counts instead of array
while (m = rx.exec(inputString)) {
const index = words.indexOf(m[0])
counts[index]++
}
Thank you for your question. I think the question in the interview was less about the right solution and more about the right approach.
The trickiest part is actually just finding the word combinations. There are several approaches here. For me it's a clear case for recursion.
So my approach would be:
find all word combinations, except combinations with itself (for example: foofoo or barbar).
iterate through the word combinations and ask whether they are contained in the string.
extra: Sort SolutionArray
Done!
Note: I use indexOf() for point 2 but I think a regex match would make it even better because you find all possibilities of a word in a string and not just the first one like with indexOf. Would make sense for longer strings.
const arr = ["foo", "bar"];
const str = "barfoothefoobarman"
let res = [];
const combinations = (len, val, existing) => {
if (len == 0) {
res.push(val);
return;
}
for(let i=0; i<arr.length; i++) {
if(! existing[i]) {
existing[i] = true;
combinations(len-1, val + arr[i], existing);
existing[i] = false;
}
}
}
const buildCombinations = (arr = []) => {
for(let i = 0; i < arr.length; i++) {
combinations(arr.length - i, "", []);
}
};
buildCombinations(arr);
// exclude the base wordes from result array
newRes = res.filter((e) => {
if (! arr.includes(e)) {
return e;
}
})
console.log('all word combinations:', newRes);
// get the string position
const _positions = [];
newRes.forEach((w) => {
let res = str.indexOf(w);
if (res != -1 && ! _positions.includes(res)) {
_positions.push(res);
}
})
// sort array and use Float64Array to speed up
const positions = new Float64Array(_positions)
console.log('positions', positions.sort())

Accounting for tiebreakers in the longest of three words array

function getLongestOfThreeWords(word1, word2, word3) {
word1 = word1.split(' ');
word2 = word2.split(' ');
word3 = word3.split(' ');
var newArr = word1.concat(word2,word3);
var LongestWord = [];
var LongestWordLength = 0;
for(var i=0; i<newArr.length; i++) {
if(newArr[i].length > LongestWordLength) {
longestWord = newArr[i];
longestWordLength = newArr[i].length;
}
}
return longestWord;
}
var output = getLongestOfThreeWords('these', 'three', 'words');
console.log(output); // --> 'these'
Got a problem I can't seem to figure out, for a longest of three words function -
"If there is a tie, it should return the first word in the tie."
Presently I'm only returning 'words', when 'these should be returned. This doesn't make sense to me because longestWordLength = newArr[i].length; Any help on this?
function getLongestOfThreeWords(word1, word2, word3) {
if(word1.length >= word2.length && word3.length) {
return word1;
} else if (word2.length > word3.length && word1.length) {
return word2;
} else {
return word3;
}
}
var output = getLongestOfThreeWords('these', 'three', 'words');
console.log(output); // --> 'these'
Great you figured out a solution.
You current solution though still is limited to three arguments only, if you want something more flexible use Rest Parameters
const getLongest = (...words) => words.sort((a, b) => b.length - a.length)[0];
console.log(getLongest('these', 'words', 'three')); // these
console.log(getLongest('super')); // super
console.log(getLongest('nice', 'and', 'super', 'flexible')); // flexible
Your first approach:
Put words in newArr
Iterate over the array, if the current item's length is greater than longestWordLength, do the updates
Note: you're updating different variables that you defined at first
function getLongestOfThreeWords(word1, word2, word3) {
const newArr = [word1, word2, word3];
let longestWord = undefined;
let longestWordLength = 0;
for(let i = 0; i < newArr.length; i++) {
if(newArr[i].length > longestWordLength) {
longestWord = newArr[i];
longestWordLength = newArr[i].length;
}
}
return longestWord;
}
const output = getLongestOfThreeWords('these', 'three', 'words');
console.log(output);
Your other approach with the correct conditions:
function getLongestOfThreeWords(word1, word2, word3) {
if(word1.length >= word2.length && word1.length >= word3.length) {
return word1;
} else if (word2.length >= word3.length) {
return word2;
} else {
return word3;
}
}
const output = getLongestOfThreeWords('these', 'three', 'words');
console.log(output);

Find all longest strings in an array [duplicate]

This question already has answers here:
Find all the words in a string which have the greatest length
(4 answers)
Closed 2 years ago.
I am trying to find all the longest strings in an array.
For inputArray = ["aba", "aa", "ad", "vcd", "aba"], the output should be
allLongestStrings(inputArray) = ["aba", "vcd", "aba"].
So far I have the code
function allLongestStrings(inputArray) {
let longboys = []
for (i = 0; i < inputArray.length; i++) {
if (inputArray[i].length) {
// what to do here
longboys.push()
}
}
return longboys
}
I am stumped on how to find the longest string and then use the length of that string to add others to the array.
Sorry if this has already been answered elsewhere, I've been finding lots of "find the longest string in an array" but nothing on finding multiple.
method
Find the longest string
Filter array for those strings with that length
function allLongestStrings(inputArray) {
let longest = Math.max(...inputArray.map(({length}) => length));
return inputArray.filter(({length}) => length === longest);
}
console.log(allLongestStrings(["aba", "aa", "ad", "vcd", "aba"]));
I'd make an object whose keys are the lengths of the strings in the values: eg { 2: ['aa', 'ad'] } - then at the end, find the largest number in the keys, and return the array there:
const allLongestStrings = (inputArray) => {
const strsByLength = {};
for (const str of inputArray) {
const { length } = str;
if (!strsByLength[length]) strsByLength[length] = [];
strsByLength[length].push(str);
}
return strsByLength[Math.max(...Object.keys(strsByLength))];
};
console.log(allLongestStrings(["aba", "aa", "ad", "vcd", "aba"]));
Try this :
function allLongestStrings(inputArray) {
const longboys = [];
let maxLength;
for (let i = 0,j = 1; i < inputArray.length - 1, j < inputArray.length; i++ , j++ ) {
if (inputArray[i].length < inputArray[j].length) {
maxLength = inputArray[i].length;
} else {
maxLength = inputArray[j].length;
}
}
for (let i = 0; i < inputArray.length; i++) {
if (inputArray[i].length === maxLength) {
longboys.push(inputArray[i]);
}
}
return longboys;
}
The previous versions look too fiddly to me and require two passes over the data. How about just:
function allLongestStrings(inputArray) {
let longboys = [];
let max_length = 0;
for (const str of inputArray) {
if (str.length > max_length) {
longboys = [];
max_length = str.length
longboys.push(str);
}
else if (str.length == max_length) {
longboys.push(str);
}
}
return longboys
};
console.log(allLongestStrings(["bb", "aba", "aa", "ad", "vcd", "aba"]));
We record all the equally long strings we've found as we go down the list, updating the maximum length as we go.
function allLongestStrings(inputArray) {
let longboys = []
var sortedArray = inputArray.sort(function(a, b){
return b.length - a.length;
});
var maxLength = sortedArray[0].length;
for(i=0;i<sortedArray.length;i++){
if(sortedArray[i].length == maxLength) {
longboys.push(sortedArray[i]);
}
else {
break;
}
}
return longboys;
}

Find Array[1D] in Array[2D] and return index

have any way can find a match index in array[2D] faster?
I know compare 1 by 1 can make this ok, but i don't wanted to.
I tried this one, but it only can return -1
// mainsheetvalues is array[1D],
[1,2,3,4,5]
// AsheetValues is array [2D]
[
[1,2,3,4,5],
[6,7,8,9,0]
]
Logger.log(mainsheetvalues.indexOf(AsheetValues))
As per this answer, we cannot compare two arrays directly. You need to generate some custom logic for the comparison. I have added a logic below. Hope this helps.
const AsheetValues = [
[1,2,3,4,5],
[6,7,8,9,0]
]
const mainsheetvalues = [1,2,3,4,5];
const isIncluded = (parentArr, childArr) => {
let isMatch = true;
for(let parentLoopIndex = 0; parentLoopIndex < parentArr.length; parentLoopIndex++) {
if (parentArr[parentLoopIndex].length != childArr.length)
isMatch = false;
for (var i = 0; i < parentArr[parentLoopIndex].length; i++) {
if (parentArr[parentLoopIndex][i] != childArr[i]) {
isMatch = false;
}
}
if (isMatch) {
parentLoopIndex = parentArr.length;
}
}
return isMatch;
}
console.log(isIncluded(AsheetValues, mainsheetvalues));

splitting a string into a multidimensional array

I have a list of strings, I want to check if the string contains a specific word, and if it does split all the words in the string and add it to an associative array.
myString = ['RT #Arsenal: Waiting for the international', 'We’re hungry for revenge #_nachomonreal on Saturday\'s match and aiming for a strong finish']
wordtoFind = ['#Arsenal']
I want to loop through the wordtoFind and if it is in myString, split up myString into individual words and create an object like
newWord = {#Arsenal:[{RT:1},{Waiting:1},{for:1},{the:1},{international:1}]}
for(z=0; z <wordtoFind.length; z++){
for ( i = 0 ; i < myString.length; i++) {
if (myString[i].indexOf(wordtoFind[z].key) > -1){
myString[i].split(" ")
}
}
}
I would say something likes would work, this also counts the amount of occurrences of a word in a sentence. JavaScript does not have associative arrays like PHP for instance. They just have objects or numbered arrays:
var myString = ['RT #Arsenal: Waiting for the international', 'We’re hungry for revenge #_nachomonreal on Saturday\'s match and aiming for a strong finish'];
var wordtoFind = ['#Arsenal'];
var result = {};
for(var i = 0, l = wordtoFind.length; i < l; i++) {
for(var ii = 0, ll = myString.length; ii < ll; ii++) {
if(myString[ii].indexOf(wordtoFind[i]) !== -1) {
var split = myString[ii].split(' ');
var resultpart = {};
for(var iii = 0, lll = split.length; iii < lll; iii++) {
if(split[iii] !== wordtoFind[i]) {
if(!resultpart.hasOwnProperty(split[iii])) {
resultpart[split[iii]] = 0;
}
resultpart[split[iii]]++;
}
}
result[wordtoFind[i]] = resultpart;
}
}
}
console.log(result);
//{"#Arsenal":{"RT":1,"Waiting":1,"for":1,"the":1,"international":1}}
This method makes use of the forEach-function and callbacks.
The containsWord-function was left with a for-loop for now to reduce some callbacks, this can obviously be changed.
var myString = [
'RT #Arsenal: Waiting for the international',
'We’re hungry for revenge #_nachomonreal on Saturday\'s match and aiming for a strong finish',
'#Arsenal: one two three four two four three four three four'
];
var wordtoFind = ['#Arsenal'];
// define the preprocessor that is used before the equality check
function preprocessor(word) {
return word.replace(':', '');
}
function findOccurences(array, search, callback, preprocessor) {
var result = {};
var count = 0;
// calculate the maximum iterations
var max = search.length * array.length;
// iterate the search strings that should be matched
search.forEach(function(needle) {
// iterate the array of strings that should be searched in
array.forEach(function(haystack) {
if (containsWord(haystack, needle, preprocessor)) {
var words = haystack.split(' ');
// iterate every word to count the occurences and write them to the result
words.forEach(function(word) {
countOccurence(result, needle, word);
})
}
count++;
// once every iteration finished, call the callback
if (count == max) {
callback && callback(result);
}
});
});
}
function containsWord(haystack, needle, preprocessor) {
var words = haystack.split(' ');
for (var i = 0; i < words.length; i++) {
var word = words[i];
// preprocess a word before it's compared
if (preprocessor) {
word = preprocessor(word);
}
// if it matches return true
if (word === needle) {
return true;
}
}
return false;
}
function countOccurence(result, key, word) {
// add array to object if it doesn't exist yet
if (!result.hasOwnProperty(key)) {
result[key] = [];
}
var entry = result[key];
// set the count to 0 if it doesn't exist yet
if (!entry.hasOwnProperty(word)) {
entry[word] = 0;
}
entry[word]++;
}
// call our function to find the occurences
findOccurences(myString, wordtoFind, function(result) {
// do something with the result
console.log(result);
}, preprocessor);
// output:
/*
{ '#Arsenal':
[ RT: 1,
'#Arsenal:': 2,
Waiting: 1,
for: 1,
the: 1,
international: 1,
one: 1,
two: 2,
three: 3,
four: 4 ] }
*/
Feel free to ask any questions, if the answer needs clarification.
I hope this fits your needs.
You're on the right track. You just need to store the split string into the associative array variable.
var assocArr = [];
for(z=0; z <wordtoFind.length; z++){
for ( i = 0 ; i < myString.length; i++) {
if (myString[i].indexOf(wordtoFind[z]) > -1){
myString[i].split(" ").forEach(function(word){
assocArr.push(word);
});
}
}
}
I think the key problem that stuck you is the data structure. The optimal structure should be something like this:
{
#Arsenal:[
{RT:1, Waiting:1, for:1, the:1, international:1},
{xxx:1, yyy:1, zzz:3}, //for there are multiple ones in 'myString' that contain the same '#Arsenal'
{slkj:1, sldjfl:2, lsdkjf:1} //maybe more
]
someOtherWord:[
{},
{},
....
]
}
And the code:
var result = {};
//This function will return an object like {RT:1, Waiting:1, for:1, the:1, international:1}.
function calculateCount(string, key) {
var wordCounts = {};
string.split(" ").forEach(function (word) {
if (word !== key) {
if (wordCounts[word] === undefined) wordCounts[word] = 1;
else wordCounts[word]++;
}
});
return wordCounts;
}
//For each 'word to find' and each string that contain the 'word to find', push in that returned object {RT:1, Waiting:1, for:1, the:1, international:1}.
wordToFind.forEach(function (word) {
var current = result[word] = [];
myString.forEach(function (str) {
if (str.indexOf(word) > -1) {
current.push(
calculateCount(str, word)
);
}
}); //Missed the right parenthesis here
});

Categories

Resources