I have an array of objects - say, NFL teams. Each object (team) has a property - array of first names of team players.
var nflTeams = [
{ name: 'Kansas City Chiefs', playersFirstNames: ['Shane', 'Chad', 'Michael', 'Ronald', 'Blake', 'Noah'], champions: true },
{ name: 'Philadelphia Eagles', playersFirstNames: ['Jalen', 'Kenneth', 'Boston', 'Trey', 'Jack', 'Andre', 'Jack', 'Lane', 'Jason', 'Nakobe'], champions: false },
{ name: 'Cincinnati Bengals', playersFirstNames: ['Brandon', 'Joe', 'Chris', 'Joe', 'Tyler', 'Trenton', 'Trent', 'Mitchell', 'Alex', 'Trey', 'Ted'], champions: false },
{ name: 'San Francisco 49ers', playersFirstNames: ['Jimmy', 'Josh', 'Kyle', 'Jordan', 'Brandon', 'Danny', 'George', 'Tyler', 'Charlie', 'Jake', 'Nick', 'Nick', 'Kevin'], champions: false },
];
Expected result - an object with an occurrence of first names: {'Joe': 1, 'Jimmy': 2, 'Jalen': 1 ....}
My goal is to achieve it through the use of Underscore methods such as _.map(), _.flatten(), _.reduce() all chained together using _.chain().
My current attempt miserably fails at _.reduce() stage:
var firstNameOccurence = { '{players firstName}': 0 };
firstNameOccurence = _.chain (nflTeams)
.map(function(team) {return team.playersFirstNames})
.flatten()
.reduce(function(newObject, firstName){
console.log ('we have a first name of a player', firstName);
return newObject[firstName] = 1 ? !newObject[firstName] : newObject[firstName] += 1;
}, {})
.value();
Now it gives me just a boolean value of true. My ideal is try to use ternary expression because it looks more elegant. I tried using official Underscore documentation and some examples like this, this and this.
I wouldn't use
.reduce()for this. Underscore.js has inbuilt methods that already perform what you're trying to do (ie: count the frequencies of items in an array), so you're better off using those instead of reinventing the wheel. What you can use is a combination of JavaScript's.flatMap()(annoyingly, underscore doesn't provide it, other utility libraries like Lodash do). And the_.countBy()method:If you don't want to use JavaScript's inbuilt
.flatMap()method you can chain.map()and.flatten()like you have:As for your attempt, I suggest looking into how the core operators of JavaScript work before looking into more complicated concepts like
.reduce(), which includes=(assignment),!(negation),? :(ternary) etc. In your attempt, you're doingnewObject[firstName] = 1 ?which assigns the value of1to thefirstNameproperty of your object as well as evaluates to the value of1. That means the true portion of your ternary will always be evaluated and will be the thing you're returning. In your case, that's!newObject[firstName]. As you just set this to1, you're negating it with!, and!1isfalse. This results in your returningfalse. Then, on the next iteration,.reduce()calls your callback withnewObjectset to thefalsevalue you just returned. It's no longer an object. Again, your code now attempts to set a propertyfirstNameonnewObjectwithnewObject[firstName] = 1. As this is equivalent tofalse[firstName] = 1, it evaluates to1, but leavesnewObject(ie:false) unmodified. When!newObject[firstName]runs again, it's unable to find the propertyfirstNameon thefalsevalue so it ends up returning!undefined, ie:true. This continues until all your iterations are completed.As you can see, your current logic in your
.reduce()callback doesn't make much sense, as you're trying to return a boolean rather than an object, which is what you want your final result to be: