How to avoid repeating "a.condition_A==b.condition_A" in if statements about "compare condition A first, if equals, then B, then C…"?

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

How to avoid repeating "a.condition_A==b.condition_A" in if statements about "compare condition A first, if equals, then B, then C..."?

问题

例如,有一段代码用于根据学生的考试成绩找到最优秀的学生,首先根据数学成绩,如果数学分数相同,则使用英语成绩,然后是西班牙语成绩:

const students = [
  {"name": "Ada", "maths": 58, "en": 70, "es": 60},
  {"name": "Bob", "maths": 70, "en": 54, "es": 42},
  {"name": "Tom", "maths": 60, "en": 50, "es": 50}
];
let bestIndex = 0;
let bestMaths = 0;
let bestEn = 0;
let bestEs = 0;
let index = 0;
for (const s of students) {
  if (s.maths > bestMaths || 
    (s.maths == bestMaths && s.en > bestEn) ||
    (s.maths == bestMaths && s.en == bestEn && s.es > bestEs)) {
    bestIndex = index;
    bestMaths = s.maths;
    bestEn = s.en;
    bestEs = s.es;
  }
  index++;
}
document.write(bestIndex);

虽然这段代码目前可以工作,但编码风格不佳,因为它重复了s.maths==bestMaths。当添加新要求时:“在比较英语成绩之前选择语言科目中获得最高分的学生”,需要重复新的“a==b”语句(在以下代码中重复s.en+s.es==bestTotalLang):

s.maths > bestMaths || 
(s.maths == bestMaths && s.en + s.es > bestTotalLang) ||
(s.maths == bestMaths && s.en + s.es == bestTotalLang && s.en > bestEn) ||
(s.maths == bestMaths && s.en + s.es == bestTotalLang && s.en == bestEn && s.es > bestEs)

还需要重新编写上面的代码“金字塔”,当条件的优先级发生变化时,例如首先选择英语最好的学生,然后是数学,然后是西班牙语。

我的问题是,是否有编码风格可以重写上面的代码,以避免重复s.maths==bestMaths(即允许s.maths==bestMaths仅出现一次),以及其他“a==b”语句?

英文:

For example, there is a piece of code to find the best student according to their exam results, with maths result first, if having the same maths score, then use en result and then es result:

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

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

const students=[
  {&quot;name&quot;:&quot;Ada&quot;,&quot;maths&quot;:58,&quot;en&quot;:70,&quot;es&quot;:60},
  {&quot;name&quot;:&quot;Bob&quot;,&quot;maths&quot;:70,&quot;en&quot;:54,&quot;es&quot;:42},
  {&quot;name&quot;:&quot;Tom&quot;,&quot;maths&quot;:60,&quot;en&quot;:50,&quot;es&quot;:50}
];
let bestIndex=0;
let bestMaths=0;
let bestEn=0;
let bestEs=0;
let index=0;
for(const s of students){
  if(s.maths&gt;bestMaths || 
    (s.maths==bestMaths &amp;&amp; s.en&gt;bestEn) ||
    (s.maths==bestMaths &amp;&amp; s.en==bestEn &amp;&amp; s.es&gt;bestEs)){
    bestIndex=index;
    bestMaths=s.maths;
    bestEn=s.en;
    bestEs=s.es
  }
  index++
}
document.write(bestIndex);

<!-- end snippet -->

While the code works fines currently, the coding style is not good looking because it is repeating

s.maths==bestMaths

. Also when adding a new requirement : "select student with highest score in all language subjects before comparing en result", new "a==b" statements is required to repeat (repeating "s.en+s.es==bestTotalLang" at the following):

 s.maths&gt;bestMaths || 
(s.maths==bestMaths &amp;&amp; s.en+s.es&gt;bestTotalLang) ||
(s.maths==bestMaths &amp;&amp; s.en+s.es==bestTotalLang &amp;&amp; s.en&gt;bestEn) ||
(s.maths==bestMaths &amp;&amp; s.en+s.es==bestTotalLang &amp;&amp; s.en==bestEn &amp;&amp; s.es&gt;bestEs)

Also it is required to rewrite the "pyramid" of code above when the priority of condition changed, for example : select student with best en first, then maths and then es.

My question is, is there any coding style to rewrite the code above, which can avoid repeating s.maths==bestMaths (ie: allow "s.maths==bestMaths" appears once only), and also for other "a==b" statements?

答案1

得分: 2

针对重复比较的一般情况,只要不涉及不纯的getter,你可以将比较结果存储在变量中,然后使用该变量。所以

s.maths > bestMaths ||
(s.maths == bestMaths && ...
(s.maths == bestMaths && ...
(s.maths == bestMaths && ...

可以改成

const thisBest = s.maths == bestMaths;
s.maths > bestMaths ||
(thisBest && ...
(thisBest && ...
(thisBest && ...

但对于你目前采用的特定逻辑,首先进行s.maths == bestMaths检查会更容易,只需在单个分支中列出其他可能性,然后跟随一个大的&&

const students = [
  { "name": "Ada", "maths": 58, "en": 70, "es": 60 },
  { "name": "Bob", "maths": 70, "en": 54, "es": 42 },
  { "name": "Tom", "maths": 60, "en": 50, "es": 50 }
];
let bestIndex = 0;
let bestMaths = 0;
let bestEn = 0;
let bestEs = 0;
let index = 0;
for (const s of students) {
  if (s.maths > bestMaths || (s.maths == bestMaths && (
    s.en > bestEn
    || (s.en == bestEn && s.es > bestEs)
    // 添加其他条件在这里
  ))) {
    bestIndex = index;
    bestMaths = s.maths;
    bestEn = s.en;
    bestEs = s.es
  }
  index++
}
console.log(bestIndex);

更好的方法是使用.reduce来保持迄今为止最佳的学生在累加器中,然后在最后记录其索引。

const students = [
  { "name": "Ada", "maths": 58, "en": 70, "es": 60 },
  { "name": "Bob", "maths": 70, "en": 54, "es": 42 },
  { "name": "Tom", "maths": 60, "en": 50, "es": 50 }
];
const bestStudent = students.reduce((a, s) => (
  (
    s.maths > a.maths ||
    (s.maths === a.maths && s.en > a.en) ||
    (s.maths === a.maths && s.en === a.en && s.es > a.es)
  ) ? s : a
));
const bestIndex = students.indexOf(bestStudent);
console.log(bestIndex);

这可能有点重复,但重复性本身并不总是敌人 - 如果牺牲可读性是替代方案,那么多少重复是可以接受的。

英文:

For the general case of a repetitive comparison, as long as impure getters aren't involved, you can store the compare result in a variable and then use that variable. So

s.maths&gt;bestMaths || 
(s.maths==bestMaths &amp;&amp; ...
(s.maths==bestMaths &amp;&amp; ...
(s.maths==bestMaths &amp;&amp; ...

could become

const thisBest = s.maths==bestMaths;
s.maths&gt;bestMaths || 
(thisBest &amp;&amp; ...
(thisBest &amp;&amp; ...
(thisBest &amp;&amp; ...

But for this particular logic you're going with, it would be easier to perform the s.maths==bestMaths check first, once, in a single branch, then list out into the other possibilities in a big &amp;&amp; that follows.

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

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

const students=[{&quot;name&quot;:&quot;Ada&quot;,&quot;maths&quot;:58,&quot;en&quot;:70,&quot;es&quot;:60},{&quot;name&quot;:&quot;Bob&quot;,&quot;maths&quot;:70,&quot;en&quot;:54,&quot;es&quot;:42},{&quot;name&quot;:&quot;Tom&quot;,&quot;maths&quot;:60,&quot;en&quot;:50,&quot;es&quot;:50}];
let bestIndex=0;
let bestMaths=0;
let bestEn=0;
let bestEs=0;
let index=0;
for (const s of students) {
  if (s.maths &gt; bestMaths || (s.maths == bestMaths &amp;&amp; (
    s.en &gt; bestEn
    || (s.en == bestEn &amp;&amp; s.es &gt; bestEs)
    // add other conditions here
  ))) {
    bestIndex = index;
    bestMaths = s.maths;
    bestEn = s.en;
    bestEs = s.es
  }
  index++
}
console.log(bestIndex);

<!-- end snippet -->

A better way to approach this would be to .reduce while keeping the best student so far in the accumulator, then log its index at the end.

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

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

const students=[{&quot;name&quot;:&quot;Ada&quot;,&quot;maths&quot;:58,&quot;en&quot;:70,&quot;es&quot;:60},{&quot;name&quot;:&quot;Bob&quot;,&quot;maths&quot;:70,&quot;en&quot;:54,&quot;es&quot;:42},{&quot;name&quot;:&quot;Tom&quot;,&quot;maths&quot;:60,&quot;en&quot;:50,&quot;es&quot;:50}];
const bestStudent = students.reduce((a, s) =&gt; (
  (
    s.maths &gt; a.maths ||
    (s.maths === a.maths &amp;&amp; s.en &gt; a.en) ||
    (s.maths === a.maths &amp;&amp; s.en === a.en &amp;&amp; s.es &gt; a.es)
  ) ? s : a
));
const bestIndex = students.indexOf(bestStudent);
console.log(bestIndex);

<!-- end snippet -->

It may be a bit repetitive, but repetitiveness is not always the enemy in itself - it's OK to be somewhat repetitive if the alternative is sacrificing readability.

答案2

得分: 0

这是您提供的代码的翻译部分:

一个可读的解决方案可以在不重复的情况下实现以下是我的处理方法

const students = [
  {"name": "Ada", "maths": 58, "en": 70, "es": 60},
  {"name": "Bob", "maths": 70, "en": 54, "es": 42},
  {"name": "Tom", "maths": 60, "en": 50, "es": 50},
  {"name": "Mary", "maths": 70, "en": 53, "es": 65}
];

const best = students.reduce((a, c) =>
  c.maths > a.maths
    ? c
    : c.maths == a.maths
      ? c.en > a.en
        ? c
        : c.en == a.en && c.es > a.es
          ? c
          : a
      : a
);

console.log(best);
console.log(students.indexOf(best));

请注意,这只是代码的翻译部分,没有包括额外的信息。

英文:

A readable solution can be achieved without repetitions. Here is my take on it:

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

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

const students=[{&quot;name&quot;:&quot;Ada&quot;,&quot;maths&quot;:58,&quot;en&quot;:70,&quot;es&quot;:60},{&quot;name&quot;:&quot;Bob&quot;,&quot;maths&quot;:70,&quot;en&quot;:54,&quot;es&quot;:42},{&quot;name&quot;:&quot;Tom&quot;,&quot;maths&quot;:60,&quot;en&quot;:50,&quot;es&quot;:50},{&quot;name&quot;:&quot;Mary&quot;,&quot;maths&quot;:70,&quot;en&quot;:53,&quot;es&quot;:65}];

const best=students.reduce((a,c)=&gt;
  c.maths&gt;a.maths
   ?c
   :c.maths==a.maths
    ?c.en&gt;a.en
     ?c
     :c.en==a.en&amp;&amp;c.es&gt;a.es
      ?c
      :a
    :a );

console.log(best);
console.log(students.indexOf(best))

<!-- end snippet -->

huangapple
  • 本文由 发表于 2023年3月1日 14:06:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/75600089.html
匿名

发表评论

匿名网友

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

确定