使用嵌套 for 循环重构函数 - Javascript

我需要删除我创建的函数中的嵌套 for 循环。我的函数接收一个关联数组,并根据某些属性返回一个新数组,以便对消息进行分组以供以后使用。例如,我有两所学校,有很多学生。所以我根据性别和年级对他们进行分组。我不知道如何重构这个函数,因为我对算法不太了解。我的逻辑是否需要被彻底抹去或者需要重新做一遍并不重要。我必须删除第二个 for 循环。另外,我可以返回公共数组、关联数组或只是对象。我尝试使用相同的逻辑但不同的数据复制我的函数:

var studentsArray = new Array();


studentsArray["SCHOOL_1"] = [

    // girls

    {id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: false},

    {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!', isMan: false},

    {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok', isMan: false},

    // boys

    {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true},

    {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},

    {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},

    {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true},

    {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true},


];

studentsArray["SCHOOL_2"] = [

    // girls

    {id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},

    {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},

    {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false},

    {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!', isMan: false},

    {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!', isMan: false},

    // boys

    {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},

    {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},

    {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true},

    {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!', isMan: true},

];


摇曳的蔷薇
浏览 129回答 3
3回答

慕哥9229398

可重复使用的模块这是了解可重用模块的绝佳机会。您的GroupMessages函数几乎超过 100 行,并且与您的数据结构紧密耦合。此答案中的解决方案解决了您的特定问题,无需对之前编写的模块进行任何修改。我将在这个答案的末尾提供一些代码审查,但现在我们重命名,因为schools数组grades中的每个项目代表特定学校单个学生的单个成绩 -const grades =&nbsp; [ {id: 1, school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: false}&nbsp; , {id: 2, school: 'SCHOOL_1', grade: 'A', message: 'Good work!', isMan: false}&nbsp; , {id: 3, school: 'SCHOOL_1', grade: 'A', message: 'Ok', isMan: false}&nbsp; , {id: 4, school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}&nbsp; , {id: 5, school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}&nbsp; , {id: 6, school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}&nbsp; , {id: 7, school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}&nbsp; , {id: 8, school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}&nbsp; , {id: 9, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}&nbsp; , {id: 10, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}&nbsp; , {id: 11, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}&nbsp; , {id: 12, school: 'SCHOOL_2', grade: 'B', message: 'Good work!', isMan: false}&nbsp; , {id: 13, school: 'SCHOOL_2', grade: 'B', message: 'Nice!', isMan: false}&nbsp; , {id: 14, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}&nbsp; , {id: 15, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}&nbsp; , {id: 16, school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}&nbsp; , {id: 17, school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!', isMan: true}&nbsp; ]正如您所知,JavaScript 没有关联数组。它也没有任何支持使用复合键查找(选择具有多个键的值)的本机数据结构。我们将从二叉树模块导入一些函数,btree为您的记录创建一个标识符,myIdentifier并使用它来初始化您的树,myTree-import { nil, fromArray, inorder } from "./btree.js"const myIdentifier = record =>&nbsp; [ record?.school ?? "noschool" // if school property is blank, group by "noschool"&nbsp; , record?.grade ?? "NA"&nbsp; &nbsp; &nbsp; &nbsp; // if grade property is blank, group by "NA"&nbsp; , record?.isMan ?? false&nbsp; &nbsp; &nbsp; &nbsp;// if isMan property is blank, group by false&nbsp; ]const myTree =&nbsp; nil(myIdentifier)二叉树根据可定制的标识符自动处理分组,并且可以使用任意数量的分组键。我们将使用 basicfilter来选择与查询匹配的所有成绩gender。fromArray选定的成绩数组与处理树更新的合并函数一起传递。inorder用于从树中提取分组值 -function groupMessages (grades, gender){ const t =&nbsp; &nbsp; fromArray&nbsp; &nbsp; &nbsp; ( myTree&nbsp; &nbsp; &nbsp; , grades.filter(x => !x.isMan || gender === "man")&nbsp; &nbsp; &nbsp; , ({ messages = [] } = {}, { message = "", ...r }) =>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ({ ...r, messages: [ ...messages, message ]})&nbsp; &nbsp; &nbsp; )&nbsp; return Array.from(inorder(t))}现在让我们看看输出 -console.log(groupMessages(grades, 'woman'))[&nbsp; {&nbsp; &nbsp; "id": "3",&nbsp; &nbsp; "school": "SCHOOL_1",&nbsp; &nbsp; "grade": "A",&nbsp; &nbsp; "isMan": false,&nbsp; &nbsp; "messages": [&nbsp; &nbsp; &nbsp; "Congratulations!",&nbsp; &nbsp; &nbsp; "Good work!",&nbsp; &nbsp; &nbsp; "Ok"&nbsp; &nbsp; ]&nbsp; },&nbsp; {&nbsp; &nbsp; "id": "11",&nbsp; &nbsp; "school": "SCHOOL_2",&nbsp; &nbsp; "grade": "A",&nbsp; &nbsp; "isMan": false,&nbsp; &nbsp; "messages": [&nbsp; &nbsp; &nbsp; "Congratulations!",&nbsp; &nbsp; &nbsp; "Congratulations!",&nbsp; &nbsp; &nbsp; "Congratulations!"&nbsp; &nbsp; ]&nbsp; },&nbsp; {&nbsp; &nbsp; "id": "13",&nbsp; &nbsp; "school": "SCHOOL_2",&nbsp; &nbsp; "grade": "B",&nbsp; &nbsp; "isMan": false,&nbsp; &nbsp; "messages": [&nbsp; &nbsp; &nbsp; "Good work!",&nbsp; &nbsp; &nbsp; "Nice!"&nbsp; &nbsp; ]&nbsp; }]为了完成这篇文章,我们将展示以下的实现btree,// btree.jsimport { memo } from "./func.js"import * as ordered from "./ordered.js"const nil =&nbsp; memo&nbsp; &nbsp; ( compare =>&nbsp; &nbsp; &nbsp; &nbsp; ({ nil, compare, cons:btree(compare) })&nbsp; &nbsp; )const btree =&nbsp; memo&nbsp; &nbsp; ( compare =>&nbsp; &nbsp; &nbsp; &nbsp; (value, left = nil(compare), right = nil(compare)) =>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ({ btree, compare, cons:btree(compare), value, left, right })&nbsp; &nbsp; )const isNil = t =>&nbsp; t === nil(t.compare)const compare = (t, q) =>&nbsp; ordered.all&nbsp; &nbsp; ( Array.from(t.compare(q))&nbsp; &nbsp; , Array.from(t.compare(t.value))&nbsp; &nbsp; )function get (t, q){ if (isNil(t))&nbsp; &nbsp; return undefined&nbsp; else switch (compare(t, q))&nbsp; { case ordered.lt:&nbsp; &nbsp; &nbsp; return get(t.left, q)&nbsp; &nbsp; case ordered.gt:&nbsp; &nbsp; &nbsp; return get(t.right, q)&nbsp; &nbsp; case ordered.eq:&nbsp; &nbsp; &nbsp; return t.value&nbsp; }}function update (t, q, f){ if (isNil(t))&nbsp; &nbsp; return t.cons(f(undefined))&nbsp; else switch (compare(t, q))&nbsp; { case ordered.lt:&nbsp; &nbsp; &nbsp; return t.cons(t.value, update(t.left, q, f), t.right)&nbsp; &nbsp; case ordered.gt:&nbsp; &nbsp; &nbsp; return t.cons(t.value, t.left, update(t.right, q, f))&nbsp; &nbsp; case ordered.eq:&nbsp; &nbsp; &nbsp; return t.cons(f(t.value), t.left, t.right)&nbsp; }}const insert = (t, q) =>&nbsp; update(t, q, _ => q)const fromArray = (t, a, merge) =>&nbsp; a.reduce&nbsp; &nbsp; ( (r, v) =>&nbsp; &nbsp; &nbsp; &nbsp;update&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ( r&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; , _ => merge ? merge(_, v) : v&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; )&nbsp; &nbsp; , t&nbsp; &nbsp; )function* inorder (t){ if (isNil(t)) return&nbsp; yield* inorder(t.left)&nbsp; yield t.value&nbsp; yield* inorder(t.right)}export { btree, fromArray, get, inorder, insert, isNil, nil, update }可重用性至关重要。模块可以导入其他模块!上面,从和btree导入- 下面包含部分模块 -funcordered// func.jsfunction memo (f){ const r = new Map&nbsp; return x =>&nbsp; &nbsp; r.has(x)&nbsp; &nbsp; &nbsp; ? r.get(x)&nbsp; &nbsp; &nbsp; : (r.set(x, f(x)), r.get(x))}export { memo }// ordered.jsconst lt =&nbsp; -1const gt =&nbsp; 1const eq =&nbsp; 0const empty =&nbsp; eqconst compare = (a, b) =>&nbsp; a < b&nbsp; &nbsp; ? lt: a > b&nbsp; &nbsp; ? gt: eqconst all = (a = [], b = []) =>&nbsp; a.reduce&nbsp; &nbsp; ( (r, e, i) =>&nbsp; &nbsp; &nbsp; &nbsp; concat(r, compare(e, b[i]))&nbsp; &nbsp; , eq&nbsp; &nbsp; )const concat = (a, b) =>&nbsp; a === eq ? b : aexport { all, compare, concat, empty, eq, gt, lt }

慕森卡

我想提供一种稍微不同的方法。它同样构建在现有功能之上,但不像该方法那样低级别。首先,这是我对问题的初步解决方案:const call = (fn, ...args) => fn (...args)const groupBy = (fn) => (xs) =>  xs .reduce ((a, x) => call (key => ((a [key] = [... (a [key] || []), x]), a), fn (x)), {})const groupMessages = (students, gender) =>   Object .values (groupBy (x => `${x.school}|${x.grade}`) (Object .values (students)     .flat ()    .filter (({isMan}) => isMan == (gender == 'man'))))    .map ((students) => ({      ... students [0],      message: students .map (s => s.message) .join ('|')    }))const students = {SCHOOL_1: [/* girls */ {id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!', isMan: false}, {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok', isMan: false}, /* boys */ {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}, {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}, {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}], SCHOOL_2: [/* girls */ {id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!', isMan: false}, {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!', isMan: false}, /* boys */ {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!', isMan: true}]}         console .log (groupMessages (students, 'woman')).as-console-wrapper {max-height: 100% !important; top: 0}它使用了groupBy我经常使用的一个功能。这又取决于call,它接受一个函数和参数列表,并使用这些参数调用该函数。(我在这里使用它只是为了将局部变量保持在最低限度。)这是有效的,并且显然比原始代码短得多。但它有一个真正的丑陋之处,这是许多此类 JS 代码所共有的。它很紧凑,但操作顺序很难看出。需要深入理解代码才能看到这一点:    const groupMessages = (students, gender) =>       Object .values (groupBy (x => `${x.school}|${x.grade}`) (Object .values (students)        /* ^-- 5 */   /* ^-- 4 */                                 /* ^-- 1 */         .flat ()  /* <-- 2 */        .filter (({isMan}) => isMan == (gender == 'man'))))  /* <-- 3 */        .map ((students) => ({  /* <-- 6 */          ... students [0],          message: students .map (s => s.message) .join ('|')        }))我倾向于使用一种pipe函数,它将一个函数的结果传递给下一个函数,并将该函数的结果传递给下一个函数,依此类推。这可以清理很多东西。但它确实需要具有具体化数组方法等内容的柯里化函数。所以,我发现这个细分更清晰:// reusable utility functionsconst pipe = (...fns) => (arg) => fns .reduce ((a, f) => f (a), arg)const map = (fn) => (xs) => xs .map (x => fn (x))const filter = (fn) => (xs) => xs .filter (x => fn (x))const call = (fn, ...args) => fn (...args)const groupBy = (fn) => (xs) =>  xs .reduce ((a, x) => call (key => ((a [key] = [... (a [key] || []), x]), a), fn (x)), {})const flat = (xs) => xs .flat ()// helper functionconst groupStudentMessages = (students) => ({  ... students [0],  message: students .map (s => s.message) .join ('|')})// main function, as a pipelineconst groupMessages = (students, gender) => pipe (  Object .values,                                     /* <-- 1 */  flat,                                               /* <-- 2 */   filter (({isMan}) => isMan == (gender == 'man')),   /* <-- 3 */  groupBy (x => `${x.school}|${x.grade}`),            /* <-- 4 */  Object .values,                                     /* <-- 5 */  map (groupStudentMessages)                          /* <-- 6 */) (students)const students = {SCHOOL_1: [/* girls */ {id: '1', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '2', school: 'SCHOOL_1', grade: 'A', message: 'Good work!', isMan: false}, {id: '3', school: 'SCHOOL_1', grade: 'A', message: 'Ok', isMan: false}, /* boys */ {id: '4', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '5', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}, {id: '6', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}, {id: '7', school: 'SCHOOL_1', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '8', school: 'SCHOOL_1', grade: 'B', message: 'Good work!', isMan: true}], SCHOOL_2: [/* girls */ {id: '9', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '10', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '11', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: false}, {id: '12', school: 'SCHOOL_2', grade: 'B', message: 'Good work!', isMan: false}, {id: '13', school: 'SCHOOL_2', grade: 'B', message: 'Nice!', isMan: false}, /* boys */ {id: '14', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '15', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '16', school: 'SCHOOL_2', grade: 'A', message: 'Congratulations!', isMan: true}, {id: '17', school: 'SCHOOL_2', grade: 'B', message: 'Congratulations!', isMan: true}]}console .log (groupMessages (students, 'woman')).as-console-wrapper {max-height: 100% !important; top: 0}我希望即使没有注释,主函数中的操作顺序也足够清晰。为了使这一点更加清晰,我们提取了groupStudentMessages辅助函数,并使每个管道步骤成为单行代码。请注意,前六个函数是非常有用的、可重用的函数。

牛魔王的故事

对我来说,你必须删除第二个 for 循环似乎很奇怪。但这仍然是关系数据库要解决的问题。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript