Reputation: 1
I'm trying to check what letters repeat in a string by creating a new array of only the repeated letters using the .filter()
method but I only want the letter to appear one time, no matter how many times it repeats.
This is what I tried:
const fullName = "Muhammad Ali";
const fullNameLowercase = fullName.toLowerCase();
const splitName = fullNameLowercase.split("");
let repeats = splitName.filter((letter, index) => {return splitName.indexOf(letter) !== index});
console.log(repeats); // prints [ 'm', 'm', 'a', 'a' ]
I want it to only add the letter once to the array repeats
, how do I do it? Is there any other more efficient way to do what I want that doesn't use .filter()
?
Upvotes: 0
Views: 873
Reputation: 24638
You can use [...new Set(repeats)]
as in the demo below:
const fullName = "Muhammad Ali";
const fullNameLowercase = fullName.toLowerCase();
const splitName = fullNameLowercase.split("");
let repeats = [...new Set(splitName.filter((letter, index) => {return splitName.indexOf(letter) !== index}))];
console.log(repeats); // prints [ 'm', 'a' ]
Alternatively ....
You can start by obtaining all unique letters in the string and then using Array#filter
to return those that repeat:
const
fullName = "Muhammad Ali",
fullNameLowercase = fullName.toLowerCase(),
uniques = [...new Set(fullNameLowercase.split(""))],
output = uniques.filter(l => fullNameLowercase.split(l).length > 2);
//OR output = uniques.filter(l => fullNameLowercase.match(new RegExp(`[${l}]`,'g')).length > 1);
console.log( output ); //[ 'm', 'a' ]
Upvotes: 0
Reputation: 44078
filter()
and .test()
filters out any non-letter characters..reduce()
returns an object that has the count of occurrences of each letter.Object.entries()
converts the object returned by .reduce()
to an array of key/values..flatMap()
filters out any letter occurring once.Example B is just a slightly smaller version of Example A
const string = `"Float like a butterfly, sting like a bee."
- Muhammad Ali`;
const repeats = text => {
let letters = text.toLowerCase().split("").filter(a => /[a-z]/.test(a));
let counts = letters.reduce((prv, cur) => {
if (!prv[cur]) {
prv[cur] = 1;
} else {
prv[cur]++;
}
return prv;
}, {});
console.log("Letter quantities: ")
console.log(counts)
return Object.entries(counts).flatMap(([key, val]) => val > 1 ? key : []);
}
console.log("Repeated letters: "+repeats(string));
const string = `"Float like a butterfly, sting like a bee."
- Muhammad Ali`;
const repeats = text => {
return Object.entries(text.toLowerCase().split("").filter(a => /[a-z]/.test(a)).reduce((prv, cur) => {
if (!prv[cur]) prv[cur] = 1;
else prv[cur]++;
return prv;
}, {})).flatMap(([key, val]) => val > 1 ? key : []);
}
console.log("Repeated letters: " + repeats(string));
Upvotes: 0
Reputation: 11347
Is there any other more efficient way to do what I want that doesn't use .filter()?
Sure, an efficient solution would be to use a counting hash table, run your string through it and collect keys those counts are > 1:
let str = "Muhammad Ali"
let counter = new Map
for (let char of str.toLowerCase())
counter.set(char, 1 + (counter.get(char) ?? 0))
let repeats = []
for (let [char, count] of counter)
if (count > 1)
repeats.push(char)
console.log(repeats)
Upvotes: 1
Reputation: 22365
maybe you can do that... ?
const repeatLetters=s=>Object.keys([...s.toLowerCase()].reduce((r,c,i,a)=>((a.indexOf(c)<i)?r[c]='':'',r),{}))
console.log( repeatLetters('Muhammad Ali') )
Upvotes: 0
Reputation: 12218
Add another Array.filter()
to the end of the line:
filter((ltr, idx, arr) => arr.indexOf(ltr) != idx);
const fullName = "Muhammad Ali";
const fullNameLowercase = fullName.toLowerCase();
const splitName = fullNameLowercase.split("");
let repeats = splitName.filter((letter, index) => {
return splitName.indexOf(letter) !== index;
}).filter((ltr, idx, arr) => arr.indexOf(ltr) != idx);
console.log(repeats);
Upvotes: 0
Reputation: 9183
Simply by wrapping up repeats
variable with Set constructor and the spread syntax: to avoid duplicates values:
const fullName = "Muhammad Ali";
const fullNameLowercase = fullName.toLowerCase();
const splitName = fullNameLowercase.split("");
let repeats = splitName.filter((letter, index) => splitName.indexOf(letter) !== index);
const repeatsNoDuplicates = [... new Set(repeats)];
console.log(repeatsNoDuplicates); // prints [ 'm', 'a']
Tip: use implicit return by remove the curly braces and return keyword from an arrow function it is implied that you return the expression code tells it to do.
Upvotes: 1
Reputation: 525
You can combine it with the reduce method like so :
const fullName = 'Muhammad Ali';
const fullNameLowercase = fullName.toLowerCase();
const splitName = fullNameLowercase.split('');
let repeats = splitName
.filter((e, i) => splitName.indexOf(e) !== i)
/* we create an empty array and for every letter :
- if the letter is already in the array: don't do anything
- if the letter isn't already in the array: add it to the array
it returns us the array without duplicates
*/
.reduce((g, c) => g.includes(c) ? g : g.concat([c]), []);
console.log(repeats);
Upvotes: 0