如何链式使用Underscore方法从嵌套数组中填充对象?

huangapple go评论74阅读模式
英文:

How to chain Underscore methods to populate an object from nested array?

问题

以下是您要求的代码部分的中文翻译:

// 假设有一个包含NFL球队信息的对象数组,每个对象都有一个名为playersFirstNames的属性,包含球队球员的名字数组。
var nflTeams = [
    { name: '堪萨斯城酋长', playersFirstNames: ['Shane', 'Chad', 'Michael', 'Ronald', 'Blake', 'Noah'], champions: true },
    { name: '费城老鹰', playersFirstNames: ['Jalen', 'Kenneth', 'Boston', 'Trey', 'Jack', 'Andre', 'Jack', 'Lane', 'Jason', 'Nakobe'], champions: false },
    { name: '辛辛那提孟加拉虎', playersFirstNames: ['Brandon', 'Joe', 'Chris', 'Joe', 'Tyler', 'Trenton', 'Trent', 'Mitchell', 'Alex', 'Trey', 'Ted'], champions: false },
    { name: '旧金山49人', playersFirstNames: ['Jimmy', 'Josh', 'Kyle', 'Jordan', 'Brandon', 'Danny', 'George', 'Tyler', 'Charlie', 'Jake', 'Nick', 'Nick', 'Kevin'], champions: false },
];

// 预期结果 - 一个包含名字出现次数的对象: {'Joe': 1, 'Jimmy': 2, 'Jalen': 1 ....}

// 我的目标是使用Underscore方法,如_.map(),_.flatten()和_.reduce(),通过_.chain()方法链来实现它。
// 我当前的尝试在_.reduce()阶段失败了:

var firstNameOccurrence = { '{球员名字}': 0 };
firstNameOccurrence = _.chain(nflTeams)
    .map(function (team) { return team.playersFirstNames; })
    .flatten()
    .reduce(function (newObject, firstName) {
        console.log('我们有一个球员的名字', firstName);
        return newObject[firstName] = 1 ? !newObject[firstName] : newObject[firstName] += 1;
    }, {})
    .value();

// 现在它只返回一个布尔值true。我的理想是尝试使用三元表达式,因为它看起来更优雅。
// 我尝试使用官方Underscore文档以及一些示例,例如这个(https://www.geeksforgeeks.org/underscore-js-_-reduce-function/),这个(https://vegibit.com/3-examples-of-the-underscore-reduce-function/)和这个(https://www.tutorialspoint.com/underscorejs/underscorejs_reduce.htm)。

请注意,上述代码中的问题是在条件表达式中使用了错误的语法。应该使用条件表达式来判断对象属性是否已经存在,然后相应地增加计数。但上述代码中的语法不正确。

英文:

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.

答案1

得分: 2

不要回答我要翻译的问题。以下是翻译好的内容:

"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:

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

If you don't want to use JavaScript's inbuilt .flatMap() method you can chain .map() and .flatten() like you have:

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('playersFirstNames').flatten().countBy().value();
console.log(res);

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 newObject (ie: false) unmodified. When !newObject[firstName] runs again, 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 boolean rather than an object, which is what you want your final result to be:

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('playersFirstNames')
  .flatten()
  .reduce((currObject, firstName) => { // ((currObject, firstName)) => ({...currObject, [firstName]: (currObject[firstName] || 0) + 1}) as the callback would also work here and it'd be more concise, however, it is less efficient as it creates a new object for each iteration as well as iterates all existing properties of `currObject` per loop.
    currObject[firstName] = (currObject[firstName] || 0) + 1;
    return currObject;
  }, {})
  .value();
console.log(res);
英文:

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:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

const nflTeams = [{ name: &#39;Kansas City Chiefs&#39;, playersFirstNames: [&#39;Shane&#39;, &#39;Chad&#39;, &#39;Michael&#39;, &#39;Ronald&#39;, &#39;Blake&#39;, &#39;Noah&#39;], champions: true }, { name: &#39;Philadelphia Eagles&#39;, playersFirstNames: [&#39;Jalen&#39;, &#39;Kenneth&#39;, &#39;Boston&#39;, &#39;Trey&#39;, &#39;Jack&#39;, &#39;Andre&#39;, &#39;Jack&#39;, &#39;Lane&#39;, &#39;Jason&#39;, &#39;Nakobe&#39;], champions: false }, { name: &#39;Cincinnati Bengals&#39;, playersFirstNames: [&#39;Brandon&#39;, &#39;Joe&#39;, &#39;Chris&#39;, &#39;Joe&#39;, &#39;Tyler&#39;, &#39;Trenton&#39;, &#39;Trent&#39;, &#39;Mitchell&#39;, &#39;Alex&#39;, &#39;Trey&#39;, &#39;Ted&#39;], champions: false }, { name: &#39;San Francisco 49ers&#39;, playersFirstNames: [&#39;Jimmy&#39;, &#39;Josh&#39;, &#39;Kyle&#39;, &#39;Jordan&#39;, &#39;Brandon&#39;, &#39;Danny&#39;, &#39;George&#39;, &#39;Tyler&#39;, &#39;Charlie&#39;, &#39;Jake&#39;, &#39;Nick&#39;, &#39;Nick&#39;, &#39;Kevin&#39;], champions: false }, ];

const res = _.countBy(nflTeams.flatMap(team =&gt; team.playersFirstNames));
console.log(res);

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js&quot; integrity=&quot;sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/script&gt;

<!-- 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: &#39;Kansas City Chiefs&#39;, playersFirstNames: [&#39;Shane&#39;, &#39;Chad&#39;, &#39;Michael&#39;, &#39;Ronald&#39;, &#39;Blake&#39;, &#39;Noah&#39;], champions: true }, { name: &#39;Philadelphia Eagles&#39;, playersFirstNames: [&#39;Jalen&#39;, &#39;Kenneth&#39;, &#39;Boston&#39;, &#39;Trey&#39;, &#39;Jack&#39;, &#39;Andre&#39;, &#39;Jack&#39;, &#39;Lane&#39;, &#39;Jason&#39;, &#39;Nakobe&#39;], champions: false }, { name: &#39;Cincinnati Bengals&#39;, playersFirstNames: [&#39;Brandon&#39;, &#39;Joe&#39;, &#39;Chris&#39;, &#39;Joe&#39;, &#39;Tyler&#39;, &#39;Trenton&#39;, &#39;Trent&#39;, &#39;Mitchell&#39;, &#39;Alex&#39;, &#39;Trey&#39;, &#39;Ted&#39;], champions: false }, { name: &#39;San Francisco 49ers&#39;, playersFirstNames: [&#39;Jimmy&#39;, &#39;Josh&#39;, &#39;Kyle&#39;, &#39;Jordan&#39;, &#39;Brandon&#39;, &#39;Danny&#39;, &#39;George&#39;, &#39;Tyler&#39;, &#39;Charlie&#39;, &#39;Jake&#39;, &#39;Nick&#39;, &#39;Nick&#39;, &#39;Kevin&#39;], champions: false }, ];

const res = _.chain(nflTeams).map(&#39;playersFirstNames&#39;).flatten().countBy().value();
console.log(res);

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js&quot; integrity=&quot;sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/script&gt;

<!-- 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 newObject (ie: false) unmodified. When !newObject[firstName] runs again, 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 boolean 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: &#39;Kansas City Chiefs&#39;, playersFirstNames: [&#39;Shane&#39;, &#39;Chad&#39;, &#39;Michael&#39;, &#39;Ronald&#39;, &#39;Blake&#39;, &#39;Noah&#39;], champions: true }, { name: &#39;Philadelphia Eagles&#39;, playersFirstNames: [&#39;Jalen&#39;, &#39;Kenneth&#39;, &#39;Boston&#39;, &#39;Trey&#39;, &#39;Jack&#39;, &#39;Andre&#39;, &#39;Jack&#39;, &#39;Lane&#39;, &#39;Jason&#39;, &#39;Nakobe&#39;], champions: false }, { name: &#39;Cincinnati Bengals&#39;, playersFirstNames: [&#39;Brandon&#39;, &#39;Joe&#39;, &#39;Chris&#39;, &#39;Joe&#39;, &#39;Tyler&#39;, &#39;Trenton&#39;, &#39;Trent&#39;, &#39;Mitchell&#39;, &#39;Alex&#39;, &#39;Trey&#39;, &#39;Ted&#39;], champions: false }, { name: &#39;San Francisco 49ers&#39;, playersFirstNames: [&#39;Jimmy&#39;, &#39;Josh&#39;, &#39;Kyle&#39;, &#39;Jordan&#39;, &#39;Brandon&#39;, &#39;Danny&#39;, &#39;George&#39;, &#39;Tyler&#39;, &#39;Charlie&#39;, &#39;Jake&#39;, &#39;Nick&#39;, &#39;Nick&#39;, &#39;Kevin&#39;], champions: false }, ];

const res = _.chain(nflTeams)
  .map(&#39;playersFirstNames&#39;)
  .flatten()
  .reduce((currObject, firstName) =&gt; { // ((currObject, firstName)) =&gt; ({...currObject, [firstName]: (currObject[firstName] || 0) + 1}) as the callback would also work here and it&#39;d be more concise, however, it is less efficient as it creates a new object for each iteration as well as iterates all existing properties of `currObject` per loop.
    currObject[firstName] = (currObject[firstName] || 0) + 1;
    return currObject;
  }, {})
  .value();
console.log(res);

<!-- language: lang-html -->

&lt;script src=&quot;https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.13.6/underscore-min.js&quot; integrity=&quot;sha512-2V49R8ndaagCOnwmj8QnbT1Gz/rie17UouD9Re5WxbzRVUGoftCu5IuqqtAM9+UC3fwfHCSJR1hkzNQh/2wdtg==&quot; crossorigin=&quot;anonymous&quot; referrerpolicy=&quot;no-referrer&quot;&gt;&lt;/script&gt;

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年5月28日 10:49:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76349748.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定