claramarsango
claramarsango

Reputation: 1

Finding repeated elements in an array using .filter() - javascript

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

Answers (7)

PeterKA
PeterKA

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

zer00ne
zer00ne

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

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));

Example B

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

gog
gog

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

Mister Jojo
Mister Jojo

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

symlink
symlink

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

TAHER El Mehdi
TAHER El Mehdi

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

Lucasbk38
Lucasbk38

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

Related Questions