Sorting and RegExp difficulty

Hello All,

Can anyone tell me why my count isn’t counting the number of occurrences of duplicates? Thanks.

// Write your numberOfOccurrences function here
function numberOfOccurrences(str) {
  let result = [];
  let count = 1;
  let strObj = {};
  let strArr = str.toLowerCase().split("").sort().join("").match(/[^!.?\s]/g);

  strArr.forEach((letter) => {
    if (strArr[letter] === strArr[letter+1]) {
      count++;
  } else if (strArr[letter] !== strArr[letter+1]) {
      count = 1;
  }
  result[letter] = count;
 });
  return result;
}

numberOfOccurrences('An apple a day. Will keep the doctor away!');

letter is the array element, not an index, you can’t do this

look at documentation, you can also have the index as function parameter if that is what you want

I think I would use reduce myself, building an object

I’ve done some error checking and found that strArr[letter] console.logs as undefined. I don’t understand why when forEach should be reading each item (letter) in the array. Can anyone explain why to me? Thanks in advance.

try to console log letter
letter is the item in the array, not an index
see my post above

Thanks. I missed your previous reply. I considered using reduce but I need to count the number of repeated elements as well as reducing it to one for each multiple occurance.

I’ve tried so many different approaches, but I can’t figure it out…I’ll see what I can figure out based on your answer. Thanks again.

first you need to use the right syntax, if you want to do t your way you can look at documentation on forEach and add the other parameters

you could also use a loop, it may be easier for you conceptually

I changed it to a regular for loop. I was trying that forEach based on a ‘solution’ type of post I fount in a search. Here’s what I have right now:

// Write your numberOfOccurrences function here
function numberOfOccurrences(str) {
  let result = [];
  let count = 1;
  let strObj = {};
  let strArr = str.toLowerCase().split("").sort().join("").match(/[^!.?\s]/g);

  for (var i = 0; i < strArr.length; i++) {
    if (strArr[i] === strArr[i+1]) {
      count++;
    } else {
        count = 1;
    }
    var key = strArr[i];
    var val = count;
    result[key] = count;
 }
  return result;
}

numberOfOccurrences('An apple a day. Will keep the doctor away!');

But this is giving me a count of all 1’s. Also, I know my for loop is only counting if the characters are one right after the other (+1), but this is why I sorted them.

I’ve updated to the following. When I console log after sorting the count, I’m getting closer correct numbers (6, 5, 4, …) but 5 should not exist and some others are off. Then, when I try to create an object with both as key:value pairs all the numbers change. Why?

// Write your numberOfOccurrences function here
function numberOfOccurrences(str) {
  let result = [];
  let countArr = [];
  let count = 1;
  let strObj = {};
  let strArr = str.toLowerCase().split("").sort().join("").match(/[^!.?\s]/g);

  for (var i = 0; i < strArr.length; i++) {
    if (strArr[i] === strArr[i+1]) {
      result.push(strArr[i]);
      count++;
    } else {
        count = 1;
        result.push(strArr[i]);
    }
  countArr.push(count);
  }
  countArr.sort(function(a,b) { return b-a});

  for (var j = 0; j < result.length; j++) {
    var key = result[j]
    strObj[key] = countArr[j];
  }
  return strObj;
}

@ilenia
So I’ve taken your advice after reading the documentation on the reduce method and I’ve changed my code to a simple reduce method. However, I’m getting errors and I’m guessing that’s because the reduce method is being assigned to a variable inside of my function. Is that why? Here’s what I’m getting now:

My code:

// Write your numberOfOccurrences function here
function numberOfOccurrences(str) {
  let strArr = str.toLowerCase().split("").sort().join("").match(/[^!.?\s]/g);
  
  let reduced = strArr.reduce(funcition (allLetters, letter) { if (letter in allLetters) { allLetters[letter]++; } else { allLetters[letter] = 1; } return allLetters;}, {});
}

numberOfOccurrences('An apple a day. Will keep the doctor away!');

The console:


Native Browser JavaScript
   
SyntaxError: Unexpected token, expected , (5:61)
   

The error:

It’s always the silly typo’s thanks @camperextraordinaire!

Okay, one more question. How do I reference the value in an object when the keys are random letters? I need to sort the results of reduced by the number of occurrences. I’ve converted the object into an array so I can use the sort method, but it’s sorting by the value of the key, not the value of the value. One more key problem is that I need to be returining an object, not an array. Once I sort the array, I need to convert back to an object. Update, I did create a variable named value.
var value = Object.values(reduced);

  reduced = [reduced];
  
  reduced.sort(function (a,b) { return b.value - a.value});
Native Browser JavaScript
   
=> [ { a: 6,
    c: 1,
    d: 2,
    e: 4,
    h: 1,
    i: 1,
    k: 1,
    l: 3,
    n: 1,
    o: 2,
    p: 3,
    r: 1,
    t: 2,
    w: 2,
    y: 2 } ]
   

: ) I’m making some progress. If anyone has any suggestions let me know.
With what I have below, I have the Object values sorted in the right order. Now, I just need to get them together correctly. I’ll reply again if I figure it out.

// Write your numberOfOccurrences function here
function numberOfOccurrences(str) {
  let strArr = str.toLowerCase().split("").sort().join("").match(/[^!.?\s]/g);
  
  let reduced = strArr.reduce(function (allLetters, letter) { if (letter in allLetters) { allLetters[letter]++; } else { allLetters[letter] = 1; } return allLetters;}, {});
  let value = Object.values(reduced).sort(function (a,b) { return b - a});
  let key = Object.keys(reduced).sort();
  reduced = {key:value};
return reduced;
}

numberOfOccurrences('An apple a day. Will keep the doctor away!');

Doesn’t really make any difference what order the object properties are in, you generally use objects for lookup (eg how many letters “a” s are there? It makes no difference if “a” is the first key or the last key), so you can get rid of all the sorting logic.

If you really want it sorted, then just populate an object you’ve already created:

{ a: 0, b: 0, c: 0, ... etc }

And pass that into reduce (this also means you could completely remove the if/else, it would just be allLetters[letter]++ for every letter.

You don’t need to sort the string either (you have to go over every letter anyway), just lowercase the string and loop over it, then check it’s a letter you’re looking at within the reducer – this doesn’t have to be a regex, but if it is, you can use /[a-z]/.test(letter)

Thanks Dan. I’ll try to impliment what you have explained. I do need it to be ordered by quantity. It is required by the assignment and I can’t pass the assignment if I don’t have them ordered by quantity. I appreciate your through explination and will do my best to apply it : ). Thanks again.

Then yeah do it afterwards (though it’s a slightly strange constraint, very uncommon, as JS object keys are only acessed in order under specific circumstances & it’s not usually very useful), eg:

return Object.fromEntries(
  Object.entries(result)
        .sort(([k1, v1], [k2, v2]) => v1 - v2)
)
1 Like

Sweet! I never though of creating array key value pairs in the sort method. I suppose because I’m pretty new to JS. So let’s make sure I understand it correctly.

  1. We created an object from the entries which are all of the result (reduced in my case) object.
  2. We sorted that object by telling the method that we want the key value pairs to follow the order I specify which instead of using a,b we are giving a a key value pair and b a key value pair.
  3. I wanted my sort from greatest to least, so then i took my value pairs from each a,b value and subtracted v1 from v2.

Is there anything else I need to understand?

Ah yep – Object.entries turns { a: 1, b: 2} into [['a', 1], ['b', 2]] (and Object.fromEntries does the opposite).

I’ve also used destructuring, which might be a bit confusing if you haven’t seen it – maybe instead

const sortedCounts = Object.entries(counts).sort(function(a, b) {
  // Each element is an array of [key, value]
  return a[1] - b[1]
});
return Object.fromEntries(sortedCounts);
1 Like

so you are setting count to one, when the last equal element is counted, and before saving the count value anywhere

I suggest you try the pythontutor tool to see what happens to your values
It is pretty useful (I am sure you can find it on google)

even if you already moved on from
this challenge I think it may be useful for you, as I see some difficulty in seeing how the variables change at each step

1 Like

Thanks for the explanation. I’ll read the documentation on those methods to gain an even better understanding. I appreciate you introducing me to those methods.

Thanks. Will python tutor help me with JS as well?