Search code examples
javascriptloopscountnumbers

JS How to create a function that count the number of hashtags


I am new to coding and have been given this question to answer.

This question is;

Create a function that receives a string that will contain a number of mentions and hashtags as on Twitter.

   E.g. "So excited to start  @coding on Monday! #learntocode #codingbootcamp"

The function should return an object describing the number of hashtags and mentions found:

   { hashtags: 2, mentions: 1 }
 

The code that I have created is this;

function countHashtagsAndMentions(str) {
   let split = str.split(" ");
    let count = 0
    for (let i = 9; i < str.length; i++) {
        let hash = split.filter(hashtag => hashtag.match(/#/g))
        if (hash === 1) {
          count ++
         }
        let total = {
        hash = count,
      }
    return hash
   }
 }

My code is being run against this;

describe("countHashtagsAndMentions", () => {
  it("returns an object", () => {
    expect(typeof countHashtagsAndMentions("")).to.equal("object");
  });
  it("returns {hashtags: 0, mentions: 0} if it finds none", () => {
    expect(
      countHashtagsAndMentions(
        "hello this is a tweet guaranteed to get very little engagement"
      )
    ).to.eql({ hashtags: 0, mentions: 0 });
  });
  it("recognises no mentions", () => {
    expect(countHashtagsAndMentions("#yolo")).to.eql({
      hashtags: 1,
      mentions: 0
    });
  });
  it("recognises no hashtags", () => {
    expect(countHashtagsAndMentions("@yobo")).to.eql({
      hashtags: 0,
      mentions: 1
    });
  });
  it("finds multiple hashtags and mentions and returns that number", () => {
    expect(countHashtagsAndMentions("#yolo @bolo #golo")).to.eql({
      hashtags: 2,
      mentions: 1
    });
    expect(countHashtagsAndMentions("@boyo #goyo @loyo #zoyo")).to.eql({
      hashtags: 2,
      mentions: 2
    });
    expect(
      countHashtagsAndMentions(
        '"So excited to start at @northcoders on Monday! #learntocode #codingbootcamp"'
      )
    ).to.eql({ hashtags: 2, mentions: 1 });
  });
});

Dose anyone have any suggestion of haw to make my code work?


Solution

  • A simple loop does it. Since you're using ES2015+ syntax, a for-of would work nicely:

    function countHashtagsAndMentions(str) {
      let hashtags = 0;
      let mentions = 0;
      for (const ch of str) {
        if (ch === "#") {
          ++hashtags;
        } else if (ch === "@") {
          ++mentions;
        }
      }
      return {hashtags, mentions};
    }
    let str = "So excited to start  @coding on Monday! #learntocode #codingbootcamp";
    console.log(countHashtagsAndMentions(str));

    That works because strings are iterable in ES2015+. The for-of loop implicitly uses the iterator from the string to walk through its characters. So within the loop, ch is each character from the string. Note that unlike str.split(), a strings iterator doens't separately the two halves of a character that requires a surrogate pair (like most emojis), which is normally what you want.

    This:

    for (const ch of str) {
        // ...
    }
    

    is effectively the same as

    let it = str[Symbol.iterator]();
    let rec;
    while (!(rec = it.next()).done) {
        const ch = rec.value;
        // ...
    }
    

    but without the it and rec variables.


    Alternately, you could use replace with a regular expression to replace all characters other than the ones you want to count. It sounds like it would be more expensive, but it's something the JavaScript engine can optimize:

    function countHashtagsAndMentions(str) {
      return {
        hashtags: str.replace(/[^#]/g, "").length,
        mentions: str.replace(/[^@]/g, "").length
      };
    }
    let str = "So excited to start  @coding on Monday! #learntocode #codingbootcamp";
    console.log(countHashtagsAndMentions(str));

    Which you use probably depends in part on the length of the string. The replace option is nice and short, but does go through the string twice.