Ինչու՞ պետք է հոգ տանել կրճատման մասին: Դա հզոր մեթոդ է, որը թույլ է տալիս կատարել բարդ փոխակերպումներ՝ առանց ավելորդ կոդ գրելու: Այս հոդվածը կբացատրի այս ֆունկցիոնալության որոշ բարդությունները և, հուսով ենք, ձեզ կստիպի, որ նախկինում ավելի շատ օգտագործեիք այն:

Առևտրային ծրագրավորողների մեծամասնությունը վաղ թե ուշ կբախվի մի տերմինի, որը կոչվում է Դեկլարատիվ ծրագրավորում: Դա մի ոճ է, որը ավագ դպրոցների և համալսարանների մեծամասնությունը չի դասավանդում, որը կենտրոնացած է ընթերցանության վրա և պատմում է ձեզ, թե ինչ է արվում՝ նկարագրելու, թեինչպես ենիրագործվում բաները:

Հաճախ, երբ հետևում ենք դեկլարատիվ մոտեցմանը, մենք կօգտագործենք ներկառուցված մեթոդներ, ինչպիսիք են Array.prototype-ում կամ տրամադրված արտաքին գրադարանի կողմից, օրինակ՝ ramdaկամ lodash— նման դեպքում մենք չգիտենք, թե ինչպես է գործառույթը ճշգրիտ իրականացվել, բայց սովորաբար դա խնդիր չէ։

Այս մեթոդներից շատերը շատ պարզ են, օրինակ՝ Array.prototype.some ստուգում է, արդյոք կա որևէ տարր, որը համապատասխանում է մեր պահանջներին և վերադարձնում է true կամ false. Array.prototype.map ստեղծում է նոր զանգված, որը պարունակում է բոլոր նախորդ տարրերը, որոնք փոփոխվել են մեր փոխանցած ֆունկցիայի համաձայն:

Բայց կա մեկ մեթոդ, որը նաև շփոթեցնում է փորձառու մշակողներին՝ Array.prototype.reduce: Փորձելով այն ապագաղտնիացնել ձեզ համար՝ մենք այն կօգտագործենք՝ իրականացնելու երեք հայտնիArray.prototype մեթոդների հիմնական տարբերակը և մեկ Object.prototype: em>մեթոդ, որն ընդունում է զանգվածը որպես մուտքագրում: Քայլ առ քայլ լուծումները համոզվելու են, որ նաև սկսնակները կհասկանան լուծումը, և Jest թեստերը, որոնք ներկայացված են պահեստում, կապացուցեն, որ նոր ստեղծված մեթոդներն աշխատում են այնպես, ինչպես պետք է:

Հրաժարում. դրանք նախատեսված չեն որպես արտադրության պատրաստի պոլիֆիլմեր օգտագործելու համար. ես փորձել եմ հնարավորինս շատ դեպքեր լուսաբանել՝ համոզվելով, որ օրինակները չափազանց բարդ չեն:

Ձեզանից ոմանք կհարցնեն՝ ի՞նչ աղմուկ է բարձրանում նվազեցնելու մասին:

JavaScript-ը շարունակում է զարգանալ; այնուամենայնիվ, որոշ մշակողներ դեռ պետք է աջակցեն ժառանգական նախագծերին: Եթե ​​մեկը ցանկանում է օգտագործել նորագույն գործառույթները, մինչդեռ, օրինակ. Աջակցելով IE9-ին, նրանք պետք է դիմեն պոլիֆիլինգի (ստեղծելով գործառույթի ներդրում այնպիսի միջավայրի համար, որը չի աջակցում այն): reduceֆունկցիան հիանալի է աշխատում այդ հարցում:

Եկեք արագ նայենք գործառույթին: Նրա կառուցման ձևը և՛ ամենաուժեղ կետն է, և՛ ամենամեծ խոչընդոտը սկզբում դրան մոտենալիս: Ֆունկցիան ինքնին ընդունում է երկու պարամետր՝ կրճատող ֆունկցիա և սկզբնական արժեք,որըընտրովի է:

Array.reduce (reducerFunction, originalValue)

Մենք մի պահ կխոսենք ռեդուկտորի ֆունկցիայի մասին, եկեք նախ կենտրոնանանք սկզբնական արժեքի վրա: Շատերը կմտածեն՝ ի՞նչն է նվազեցնում այդքան հզոր: Պատասխաններից մեկը հենց սկզբնական արժեքի մեջ է: Զանգվածի այլ մեթոդներ վերադարձնում են որոշակի տեսակ — քարտեզ, զտիչ, հարթ և այլն կվերադարձնենզանգված; ոմանք, յուրաքանչյուր վերադարձնում է Բուլյան (ճշմարիտ/սխալ):
Նվազեցնելըթույլ է տալիս մեզ վերադարձնել ցանկացած տեսակի արժեք, և տեսակը որոշող գործոնը մեր սկզբնական արժեքն է —տարբեր սկզբնական արժեքներ տրամադրելու դեպքում այն ​​տարբեր արդյունքներ կտա: Շլացուցիչ?

կրճատիչ ֆունկցիանինքնընդունում է չորսպարամետր:

  • կուտակիչ. արժեքը, որն ի վերջո կվերադարձվի կատարումից հետո, որը կուտակում է արժեքները: Առաջին քայլում կուտակիչը հավասար կլինի initialValue-ին,
  • ընթացիկ արժեք— այս համատեքստում ընթացիկի նշանակությունը կբացատրվի մի պահ,
  • ընթացիկ ինդեքս —ինչպես վերևում,
  • բնօրինակ (մուտքագրված) զանգված:

Ջերմացման օրինակ

Եկեք սկսենք հեշտ առաջադրանքից. մեզ տրված է թվերի զանգված, որոնք ցանկանում ենք գումարել: Իմպերատիվ մոտեցումը, որը ուսուցանվում է ավագ դպրոցներում, հավանաբար կօգտագործի for հանգույցը: Մենք կրկնում էինք զանգվածի յուրաքանչյուր տարրի վրա և ավելացնում այդ տարրը օգնական փոփոխականին: Եվ այստեղ է, որ ոչ տեխնիկական մարդիկ կշփոթվեն:

Այս մոտեցումն արդեն որոշակի գիտելիքներ է պահանջում: Թեև դա լավ է CS101-ի համար, միևնույն ժամանակ մի փոքր չափազանց լայն է այն, ինչ անում է: Պետք է իմանալ i++-ի դերը, ինչ է նշանակում [i]հետո զանգվածը, և ինչու է i առաջին հերթին վկայակոչվել է. Տեսնենք, թե ինչպես կրճատումը կարող է օգնել մեզ այստեղ:

Տեսնենք, թե ինչ է անում այս գործառույթը, քայլ առ քայլ: Հանդիպեք console.log, մի ֆունկցիա, որը թույլ է տալիս մեզ դուրս բերել գործընթացի յուրաքանչյուր քայլ: Մենք կավելացնենք վերոհիշյալ երրորդ կրճատիչ փոփոխականը՝ ինդեքսը, որպեսզի ցույց տանք, թե ինչ է տեղի ունենում յուրաքանչյուր կրկնության հետ:

Ուշադրություն դարձրեք, որ այն արժեքը, որը մենք վերադարձնում ենք յուրաքանչյուր կրկնությամբ, դառնում է նոր կուտակիչ: Ուշադրություն դարձրեք, որ, ինչպես նշվեց նախկինում, կուտակիչը հավասար է սկզբնական արժեքին առաջին կրկնության մեջ:

Քանի որ Reduce 101-ը մեզ հետ է մնում, եկեք գործի անցնենք:

քարտեզ

Այսպիսով, ինչպե՞ս ենք մենք վերստեղծում Array.prototype.mapօգտագործելով reduce: Այն, ինչ անում է քարտեզը, հետևյալն է.

· այն վերցնում է յուրաքանչյուր տարր և կիրառում է փոխանցված գործառույթը,

· Այնուհետև այն վերադարձնում է նոր զանգված՝ փոփոխված տարրերով:

Ենթադրենք, մենք ունենք թվերի զանգված, և մենք կցանկանայինք յուրաքանչյուրը բազմապատկել երեքով: Մենք կարող ենք դրան հասնել հետևյալ գործառույթով.

Մենք այստեղ օգտագործում ենք սլաքի ֆունկցիայի արտահայտությունը շարահյուսությունը, որի շնորհիվ մենք ստիպված չենք լինում օգտագործել գանգուր փակագծեր և վերադարձկարճ գործառույթների համար: Յուրաքանչյուր թիվը բազմապատկվում է 3-ով և վերադարձվում: Զանգվածների երկարությունները առաջ և հետո հավասար են: Ինչպե՞ս ենք դրան հասնում նվազեցնելով:

Այն, ինչ մենք պետք է անենք, հետևյալն է.

· ստեղծել զանգված— սկզբնական արժեքը պետք է լինի դատարկ զանգված, այլապես concat ֆունկցիան սխալ կառաջացնի,

· Վերցրեք մուտքային զանգվածի տարրերը և փոփոխեք դրանք ըստ անցած ֆունկցիայի (այսուհետև մենք կանվանենք որպես պարամետր փոխանցված գործառույթներ հետազանգեր),

· ավելացրեք փոփոխված տարրերը մեր կուտակիչ զանգվածին, որն այնուհետև կվերադարձվի:

ConcatArray.prototype մեթոդ է, որը թույլ է տալիս մեզ միաձուլել գոյություն ունեցող զանգվածը մեկ այլի հետ կամ փոխանցել արժեքներ: Թեստերով ծածկագիրը կարելի է մուտք գործել այստեղ:

զտիչ

Զտիչը վերադարձնում է նոր զանգված, ներառյալ այն տարրերը, որոնք բավարարում են մեր պահանջը: Ինչպե՞ս ենք դրան հասնում: Ստեղծելով զտիչ ֆունկցիա, որը վերադարձնում է true, երբ պահանջները բավարարվում են:

Այն, ինչ մենք հիմնականում անում ենք, հետևյալն է.

· ստեղծել զանգված,

· Վերցրեք տարրեր մուտքային զանգվածից և ստուգեք դրանք տրամադրված գործառույթով,

· երբ ընթացիկ արժեքը զտիչ հետ կանչելու ֆունկցիան փոխանցելու արդյունքը ճշմարիտ է,արժեքը ավելացվում է կուտակիչին, հակառակ դեպքում մենք թողնում ենք կուտակիչ ինչպես որ կա:

Թեստերով ծածկագիրը կարելի է մուտք գործել այստեղ:

Ի՞նչ գործ ունի այս հարցականի և երկու կետի հետ, հանկարծ:

կարելի է հարցնել. եռակի օպերատորըդյուրանցում է ավանդական if/elseպայմանական դրույթի համար: Եթե ​​հարցական նշանից առաջ փոխանցված արժեքը (այստեղ callback(currentValue)) truthy է, այն վերադարձնում է արժեքը հարցական նշանից հետո, այս դեպքում՝ միացում կուտակիչը և ընթացիկ արժեքը, հակառակ դեպքում արժեքը երկու կետից հետո, որն ինքնին կուտակիչն է:

Չափազանցություն չի լինի ասել, որ TC39 կոմիտեն և բոլորը, ովքեր ներգրավված են ECMA-ի նոր հնարավորությունների աջակցման մեջ, հերոսներ են JavaScript-ի բոլոր մշակողների համար՝ անընդհատ տրամադրելով մեր աշխատանքը հեշտացնող գործառույթներ: Ես խոստացա տրամադրել նոր, չաջակցվող գործառույթներ օգտագործելու միջոց. և՛ Array.prototype.flat և Object.fromEntries-ը հզոր են, բայց չեն աջակցվում IE-ում:

գրառումներից

Իմանալու համար, թե ինչ է անում fromEntries-ը, ավելի լավ է նախ տեսնել, թե ինչպես է աշխատում Object.entriesը:

Օբյեկտը փոխակերպվում է (yikes) զանգվածների զանգվածի, որոնցից յուրաքանչյուրում պահվում է բանալինև արժեքը զույգ: Ինչպե՞ս վերադառնանք սկզբնական օբյեկտին: Առավել հաջողակները՝ օգտագործելով Object.prototype.fromEntries():

Ժառանգության կողմնակիցները պետք է այլ ճանապարհ գտնեն դրան հասնելու համար։ Ինչպե՞ս:

Մենք տեսնում ենք, որ պարզ օբյեկտների համար մուտքերի ելքը միշտ նույնն է: Զանգվածի առաջին տարրը ինդեքսն է, երկրորդը՝ արժեքը։ Դուք կարող եք կարդալ կետային շարահյուսության մասին, որը մենք օգտագործում ենք ստորև բերված օրինակում այստեղ:

Բայց ինչ վերաբերում է խորը բնադրված առարկաներին:

Քանի որ Object.entries-ը չի փոխակերպում տեղադրված օբյեկտները զանգվածների, մեր ալգորիթմը նույնպես պատրաստված է տեղադրված տվյալների դեպքերի համար: Թեստերով ծածկագիրը կարելի է մուտք գործել այստեղ:

հարթ

Պարզապես FYI — այս իրականացումը կլինի ավելի երկար և ավելի մանրամասն, քան նախորդները: Քեզ մի մտածիր, եթե սկզբում չես հասկանում, այն նաև ներառում է մտածողության գործընթացը, որն անհրաժեշտ է լուծում ստանալու համար, որն ընդգրկում է դեպքերի մեծ մասը և ներկայացնում է որոշ հնարքներ:

Array.prototype.flatհետաքրքիր ֆունկցիա է. ինչպես անունն է հուշում, այն կհարթեցնի (նվազեցնի խորությունը) զանգվածը այնքան անգամ, որքան մենք մտադիր ենք:

Եթե ​​որևէ արգումենտ չի փոխանցվում Array.prototype.flat-ին,ապա զանգվածը հարթվում է մեկ մակարդակով: Համոզվելու համար, որ ցանկացած զանգված կլինի առավելագույնս հարթեցված, մենք անվանում ենք հարթ (անսահմանություն): Սա ամենից շփոթեցնողը կլինի, այնպես որ կապեք ձեր ամրագոտիները: Առաջին փորձն ունի հետևյալ տեսքը (մի անհանգստացեք, մենք այն կբարելավենք).

Այսպիսով, մենք ստացել ենք չսահմանված և թվերի համահարթեցում: Բայց ինչ վերաբերում է այլ արժեքներին: Ահա թե որտեղ է այն կեղտոտվում:

Այն, ինչ ես պարզեցի, այն է, որ այդ հարթվերաբերվում է բոլոր կեղծ արժեքներին, բացառությամբ չսահմանված և բոլոր այլ արժեքների, ընդհանուր առմամբ, բացառությամբ 0-ից մեծ թվերի և Date օբյեկտի ինչպես դա կլիներ flat(0)-ի դեպքում: Ինչպես հավանաբար կռահեցիք, մեր սցենարը պատրաստ չէ դրան (դեռ):

Մենք բազմաթիվ ստուգումներ ենք իրականացնում այստեղ՝

  • ստուգելով, թե արդյոք ժամանակները կեղծ են, բայց ոչ չսահմանված,
  • ստուգելով, թե արդյոք timesդատարկ օբյեկտ է,
  • ստուգելով, թե արդյոք timesդատարկ զանգված է,
  • ստուգելով, թե արդյոք անգամները զրոյից ցածր թիվ է,
  • ստուգելով, թե արդյոք times-ը դատարկ տող է:

Այս դեպքերում մենք պետք է վերադարձնենք սկզբնական զանգվածը: Եվ դա ընդգրկում է դեպքերի մեծ մասը, թեև բավականին սարսափելի:

Կա մեկ (իրականում, երկու) խնդիր. եթե timesհավասար է Number.POSITIVE_INFINITYապա մեր do/while հանգույցը երբեք չի դադարի կրկնվել: Բացի այդ (դա երկրորդ խնդիրն է), Dateօբյեկտները դիտարկվում են որպես ամբողջ թիվ բնիկ հարթ ֆունկցիայի կողմից, որը մեկնաբանում է դրանք որպես 1970 թվականի հունվարի 1-ի կեսգիշերից սկսած միլիվայրկյանների թիվ (մոտ 1,64 * 10¹² մինչ այժմ: 19.01.2022):
Այս երկու դեպքերում էլ մենք ուզում ենք ինչ-որ կերպ ստուգել, ​​թե արդյոք օղակը դեռ պետք է շարունակի: Բացի այդ, թվում է, որ ավելի քիչ դեպքեր կան, երբ մենք իրականում ցանկանում ենք կատարել հարթեցում, քան այնտեղ, որտեղ մենք ցանկանում ենք վերադարձնել մուտքային զանգվածը, ուստի մենք կպարզեցնենք նաև առաջին ստուգումը:

Լավ, բայց ինչպե՞ս իմանանք, որ մեր գործառույթը կտա նույն արդյունքները, ինչ հայրենիը: Ահա, որտեղ միավորի թեստերը հարմար են: Քանի որ այս թեստերը մի փոքր ավելի բարդ են, քան նախորդները, դրանք նկարագրվելու են քայլ առ քայլ:

Մենք պետք է որոշ տվյալներ ստեղծենք, որոնք մենք կօգտագործենք՝ ստուգելու համար, թե արդյոք մեր գործառույթը ճիշտ է աշխատում: Սա նշանակում է, որ մենք պետք է լուսաբանենք խորության և անկանոն բնադրման մի քանի դեպքեր։
Նախ, մենք ստեղծում ենք որոշ մեթոդներ և փոփոխականներ, որոնք կօգնեն մեզ փորձարկել եզրային դեպքերը և ստեղծել տվյալներ:

Մեր առաջին մեթոդը arrayOfNumbers ստեղծում է թվերի զանգված՝ սկսած 1-ից մինչև այն թիվը, որը մենք տրամադրում ենք որպես երկարություն, ուստի այն անվանելով 3 վերադարձ [1,2,3] և այլն: Այնուհետև մենք ստեղծում ենք զանգված, որը մենք կօգտագործենք ստուգելու համար, թե արդյոք շատ խորը տեղադրված զանգվածը ճիշտ հարթեցված է:
Վերջին փոփոխականը և ֆունկցիան կարող են սարսափելի թվալ, ուստի թույլ տվեք բացատրել դրանք օրինակով:

Մեր nestedArrayGenerator ֆունկցիան օգտագործում է reduceRight,որը հիմնականում աշխատում է reduce-ի նման,բայց սկսում է կրկնվել վերջին տարրից: Յուրաքանչյուր կրկնության ժամանակ ֆունկցիան ստեղծում է զանգված, որտեղ առաջին տարրը ընթացիկ տարրն է, իսկ երկրորդ տարրը՝ կուտակիչ զանգվածը։ Եթե ​​կուտակիչի զանգվածը դատարկ է (առաջին քայլ), ապա մենք ուղղակի տեղադրում ենք ընթացիկ տարրը ներսում։

Այնուհետև ես ստեղծեցի ևս մի քանի տվյալներ՝ օգտագործելով այլ բնադրման օրինաչափություն. փոփոխականների առաջին խումբը հետևում է սիմետրիկ բնադրման օրինակին, իսկ երկրորդը՝ ալիքային:

Այնուհետև մենք ստեղծում ենք դեպքեր, որպեսզի ստուգեն մեր թեստերը. այս դեպքերից յուրաքանչյուրը կլինի զանգված, որը կպարունակի մուտքային զանգվածը, որը մենք տրամադրում ենք մեր հարթֆունկցիայի, խորության արգումենտին, որը մենք կօգտագործենք և մեր ակնկալածին: ելքային զանգված. Ես նաև ներկայացրել եմ դեպքեր, որոնք կարող են խափանել մեր գործառույթը:

Վերջին բաց թողնված կտորը հենց բուն թեստն է: Ես օգտագործել եմ Jest-ի տրամադրածtest.eachֆունկցիան, բայց հնարավոր է նաև օգտագործել forEachcasesզանգվածում:
Երկրորդ յուրաքանչյուրկանչն ընդունում է երկու պարամետր՝ տող, որը բացատրում է փորձարկված դեպքը և հաստատման ֆունկցիա, որը կկատարի իրական թեստը: Յուրաքանչյուր %pառաջին տողի մեջ ներկայացնում է արգումենտ պնդման ֆունկցիայի մեջ:

5-րդ տողում մենք ստեղծում ենք ներկառուցված հարթ ֆունկցիայի օգտագործման արդյունք, իսկ 6-րդ տողում՝ մեր հատուկ ֆունկցիան օգտագործելու արդյունք: Այնուհետև մենք ստուգում ենք, թե արդյոք երկու զանգվածները հավասար են այն զանգվածին, որը մենք ակնկալում ենք:

Թեստերով ծածկագիրը կարելի է գտնել այստեղ:

Ինչպես տեսնում եք, մենք կարողացանք շատ բանի հասնել նվազեցնելու ուժի շնորհիվ (և do/whileվերջին օրինակում ես պետք է ընդունեմ հրամայականը. ոճը հարմար եկավ): Հուսով եմ, որ դուք սովորել եք, թե որքան հզոր է reduceը, որ անհապաղ ահաբեկումը վերացել է, և որ դուք կսկսեք օգտագործել այն ձեր նախագծերում: