I wouldn't use `.reduce()` for this. UnderscoreJS 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 of 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 libraries like lodash do). And the `_.countBy()` method:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const 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 }, ];
const res = _.countBy(nflTeams.flatMap(team => team.playersFirstNames));
console.log(res);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js" integrity="sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- end snippet -->
If you don't want to use JavaScript's inbuilt `.flatMap()` method you can chain `.map()` and `.flatten()` like you have:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const 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 }, ];
const res = _.chain(nflTeams).map(team => team.playersFirstNames).flatten().countBy().value();
console.log(res);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js" integrity="sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- end snippet -->
---
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 doing `newObject[firstName] = 1 ?` which assigns the value of `1` to the `firstName` property of your object as well as evaluates to the value of `1`. 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 to `1`, you're negating it with `!`, and `!1` is `false`. This results in your returning `false`. Then, on the next iteration, `.reduce()` calls your callback with `newObject` set to the `false` value you just returned. It's no longer an object. Again, your code now attempts to set a property `firstName` on `newObject` with `newObject[firstName] = 1`. As this is equivalent to `false[firstName] = 1` it `=` evaluates to `1`, but leaves the `newObject` (ie: `false`) unmodified. When `!newObject[firstName]` runs, it's unable to find the property `firstName` on the `false` value 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 boolea rather than an object, which is what you want your final result to be:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const 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 }, ];
const res = _.chain(nflTeams)
.map(team => team.playersFirstNames)
.flatten()
.reduce((currObject, firstName) => {
currObject[firstName] = (currObject[firstName] || 0) + 1;
return currObject;
}, {})
.value();
console.log(res);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js" integrity="sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- end snippet -->
Performance
===========
Today 2023.3.20 I perform tests for chosen solutions on MacOs Monterey 12.1 (M1, 16GB) on Chrome v109, Safari v15.2 and Firefox v110.
Conclusions
-----------
* Solutions based on `for-in` (A, L) are fast or fastest on all browsers
* Solution based on `JSON.stringify` (B) is slowest on all browsers
[![enter image description here][1]][1]
Details
=======
There solutions are presented in the snippet below.
If you want to run a performance test on your machine, click
* [HERE](https://jsbench.me/qfkqv692c8/1) for empty object
* [HERE](https://jsbench.me/uolfgrn0gg) for object witch 10 fields
* [HERE](https://jsbench.me/pblfgtco9o/1) for object with 100 fields
Old version of this answer contains tests from 2020 - [HERE](https://stackoverflow.com/revisions/59787784/7).
Links to answers:
[A](https://stackoverflow.com/a/785768/860099),
[B](https://stackoverflow.com/a/14488421/860099),
[C](https://stackoverflow.com/a/785768/860099),
[D](https://stackoverflow.com/a/61073221/860099),
[E](https://stackoverflow.com/a/19813797/860099),
[F](https://stackoverflow.com/a/62976784/860099),
G,
H,
[I](https://stackoverflow.com/a/51061556/860099),
[J](https://stackoverflow.com/a/58448172/860099),
[K](https://stackoverflow.com/a/10945139/860099),
[L](https://stackoverflow.com/a/679937/860099),
[M](https://stackoverflow.com/a/55765589/860099),
[N](https://stackoverflow.com/a/55765589/860099),
[O](https://stackoverflow.com/a/26464582/860099)
[P](https://stackoverflow.com/a/4794603/860099)
[Q](https://stackoverflow.com/a/5397515/860099)
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
var log = (s, f) => console.log(`${s} --> {}:${f({})} {k:2}:${f({ k: 2 })}`);
function A(obj) {
for (var i in obj) return false;
return true;
}
function B(obj) {
return JSON.stringify(obj) === "{}";
}
function C(obj) {
return Object.keys(obj).length === 0;
}
function D(obj) {
return Object.entries(obj).length === 0;
}
function E(obj) {
return Object.getOwnPropertyNames(obj).length === 0;
}
function F(obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
function G(obj) {
return typeof obj === "undefined" || !Object.keys(obj)[0];
}
function H(obj) {
return Object.entries(obj).length === 0 && obj.constructor === Object;
}
function I(obj) {
return Object.values(obj).every((val) => typeof val === "undefined");
}
function J(obj) {
for (const key in obj) {
if (hasOwnProperty.call(obj, key)) {
return false;
}
}
return true;
}
function K(obj) {
var isEmpty = true;
for (keys in obj) {
isEmpty = false;
break;
}
return isEmpty;
}
function L(obj) {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) return false;
}
return true;
}
function M(obj) {
if (obj === null || typeof obj !== 'object' ||
Object.prototype.toString.call(obj) === '[object Array]') {
return false
} else {
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
return false
}
}
return JSON.stringify(obj) === JSON.stringify({})
}
}
function N(obj) {
return (
Object.getOwnPropertyNames(obj).length === 0 &&
Object.getOwnPropertySymbols(obj).length === 0 &&
Object.getPrototypeOf(obj) === Object.prototype
);
}
function O(obj) {
return !(Object.getOwnPropertyNames !== undefined
? Object.getOwnPropertyNames(obj).length !== 0
: (function () {
for (var key in obj) break;
return key !== null && key !== undefined;
})());
}
function P(obj) {
return $.isEmptyObject(obj)
}
function Q(obj) {
return _.isEmpty(obj);
}
log("A", A);
log("B", B);
log("C", C);
log("D", D);
log("E", E);
log("F", F);
log("G", G);
log("H", H);
log("I", I);
log("J", J);
log("K", K);
log("L", L);
log("M", M);
log("N", N);
log("O", O);
log("P", P);
log("Q", Q);
<!-- language: lang-html -->
<script
src="https://code.jquery.com/jquery-3.6.4.min.js"
integrity="sha256-oP6HI9z1XaZNBrJURtCoUT5SUnxFr8s3BzRl+cbzUq8="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js" integrity="sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<!-- end snippet -->
[![enter image description here][2]][2]
[1]: https://i.stack.imgur.com/ITeDZ.png
[2]: https://i.stack.imgur.com/JO8Le.png