mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-26 08:03:56 -05:00
feat: rewrite functional spreadsheet project (#49970)
Co-authored-by: Jessica Wilkins <67210629+jdwilkin4@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Introduction to the Learn Functional Programming by Building a Spreadsheet
|
||||
block: learn-functional-programming-by-building-a-spreadsheet
|
||||
superBlock: JavaScript Algorithms and Data Structures
|
||||
isBeta: true
|
||||
---
|
||||
|
||||
## Introduction to the Learn Functional Programming by Building a Spreadsheet
|
||||
|
||||
This is a test for the new project-based curriculum.
|
||||
@@ -10,571 +10,416 @@
|
||||
"template": "",
|
||||
"required": [],
|
||||
"superBlock": "2022/javascript-algorithms-and-data-structures",
|
||||
"superOrder": 4,
|
||||
"isBeta": true,
|
||||
"challengeOrder": [
|
||||
[
|
||||
"5d79253297c0ebb149ea9fed",
|
||||
"642db8c409d9991d0b3b2f0d",
|
||||
"Step 1"
|
||||
],
|
||||
[
|
||||
"5d7925323be8848dbc58a07a",
|
||||
"642dccb78549c9285835ebc2",
|
||||
"Step 2"
|
||||
],
|
||||
[
|
||||
"5d792532f631702ae6d23e11",
|
||||
"642ddfdea4200e313f80a4b6",
|
||||
"Step 3"
|
||||
],
|
||||
[
|
||||
"5d7925329445167ecc2ac9c9",
|
||||
"642def66e6a60432c9a0371e",
|
||||
"Step 4"
|
||||
],
|
||||
[
|
||||
"5d792532b07918c3a5904913",
|
||||
"642df32c0c2db433d8b46d46",
|
||||
"Step 5"
|
||||
],
|
||||
[
|
||||
"5d792533cc8b18b6c133edc7",
|
||||
"642df9df4b5216350de7b0d2",
|
||||
"Step 6"
|
||||
],
|
||||
[
|
||||
"5d7925337954ed57a565a135",
|
||||
"642dfb07e7fa6736251541c8",
|
||||
"Step 7"
|
||||
],
|
||||
[
|
||||
"5d79253352e33dd59ec2f6de",
|
||||
"642e0011c45c893845842058",
|
||||
"Step 8"
|
||||
],
|
||||
[
|
||||
"5d792533d31e4f7fad33011d",
|
||||
"642e004130958c3975aa3a4a",
|
||||
"Step 9"
|
||||
],
|
||||
[
|
||||
"5d792533e7707b9645d7b540",
|
||||
"642e02be7845f13b014cd2b0",
|
||||
"Step 10"
|
||||
],
|
||||
[
|
||||
"5d79253378595ec568f70ab6",
|
||||
"6434552bcc0a951a0a99df3b",
|
||||
"Step 11"
|
||||
],
|
||||
[
|
||||
"5d7925330918ae4a2f282e7e",
|
||||
"64345b810a6e481e5e326849",
|
||||
"Step 12"
|
||||
],
|
||||
[
|
||||
"5d792533ed00e75d129e1b18",
|
||||
"64345c560591891f64976f7a",
|
||||
"Step 13"
|
||||
],
|
||||
[
|
||||
"5d792533a5c42fb4d1a4b70d",
|
||||
"64347464f78cd9209545f35c",
|
||||
"Step 14"
|
||||
],
|
||||
[
|
||||
"5d79253358e8f646cbeb2bb0",
|
||||
"6434750c53db16218f41e6e1",
|
||||
"Step 15"
|
||||
],
|
||||
[
|
||||
"5d792533bb38fab70b27f527",
|
||||
"6434759f78ec812264ff8f34",
|
||||
"Step 16"
|
||||
],
|
||||
[
|
||||
"5d79253386060ed9eb04a070",
|
||||
"643475e13dc727231acd0f72",
|
||||
"Step 17"
|
||||
],
|
||||
[
|
||||
"5d792533717672657b81aa69",
|
||||
"643498328cb52026123e2b91",
|
||||
"Step 18"
|
||||
],
|
||||
[
|
||||
"5d7925335ab63018dcec11fe",
|
||||
"643498755d54c6279ba09078",
|
||||
"Step 19"
|
||||
],
|
||||
[
|
||||
"5d7925330f300c342315066d",
|
||||
"6437124c4c03dd4c8fb35d56",
|
||||
"Step 20"
|
||||
],
|
||||
[
|
||||
"5d792533aa6443215c9b16bf",
|
||||
"6437133052eaf04d7300e622",
|
||||
"Step 21"
|
||||
],
|
||||
[
|
||||
"5d7925334c5e22586dd72962",
|
||||
"643715013330824ecaa70442",
|
||||
"Step 22"
|
||||
],
|
||||
[
|
||||
"5d79253307ecd49e030bdcd1",
|
||||
"64496d1e5af8c0148fbef96d",
|
||||
"Step 23"
|
||||
],
|
||||
[
|
||||
"5d792534257122211d3043af",
|
||||
"64496d80bc174a158c973080",
|
||||
"Step 24"
|
||||
],
|
||||
[
|
||||
"5d7925346f4f2da6df4354a6",
|
||||
"64496df724dd3716a71fe971",
|
||||
"Step 25"
|
||||
],
|
||||
[
|
||||
"5d792534cac2dbe0a719ea7a",
|
||||
"64496e9c6d7a2e189948e441",
|
||||
"Step 26"
|
||||
],
|
||||
[
|
||||
"5d792534857332d07ccba3ad",
|
||||
"6449749d20436c1f1dfadcf2",
|
||||
"Step 27"
|
||||
],
|
||||
[
|
||||
"5d792534d586ef495ea9df90",
|
||||
"6449755666005520330cec5b",
|
||||
"Step 28"
|
||||
],
|
||||
[
|
||||
"5d79253410532e13d13fe574",
|
||||
"64497da4062602213ecf32e7",
|
||||
"Step 29"
|
||||
],
|
||||
[
|
||||
"5d7925342415527083bd6667",
|
||||
"64497de936a2f322327e5c58",
|
||||
"Step 30"
|
||||
],
|
||||
[
|
||||
"5d792534c3d26890ac1484d4",
|
||||
"64497e0e5e5a2c2329785af4",
|
||||
"Step 31"
|
||||
],
|
||||
[
|
||||
"5d792534b92f3d1cd4410ce3",
|
||||
"64497e764135bd24b7960dd3",
|
||||
"Step 32"
|
||||
],
|
||||
[
|
||||
"5d7925341193948dfe6d76b4",
|
||||
"6449842c6f6c84261075e4c9",
|
||||
"Step 33"
|
||||
],
|
||||
[
|
||||
"5d792534cf81365cfca58794",
|
||||
"64498473a17adc26ef0ecc2d",
|
||||
"Step 34"
|
||||
],
|
||||
[
|
||||
"5d7925348ee084278ff15556",
|
||||
"6449849b78f43527be1e8a98",
|
||||
"Step 35"
|
||||
],
|
||||
[
|
||||
"5d7925348a6a41c32f7a4e3e",
|
||||
"64498542cab69128ab24e4de",
|
||||
"Step 36"
|
||||
],
|
||||
[
|
||||
"5d792534408c5be896b0a46e",
|
||||
"6449860d84c9e22cbd7b497c",
|
||||
"Step 37"
|
||||
],
|
||||
[
|
||||
"5d792534f0eda837510e9192",
|
||||
"6449863f592af72d9be0959e",
|
||||
"Step 38"
|
||||
],
|
||||
[
|
||||
"5d7925346b911fce161febaf",
|
||||
"6449874d5191562eb3313b3f",
|
||||
"Step 39"
|
||||
],
|
||||
[
|
||||
"5d79253483eada4dd69258eb",
|
||||
"6449876e7aae0d2f8257a497",
|
||||
"Step 40"
|
||||
],
|
||||
[
|
||||
"5d7925342b2b993ef18cd45f",
|
||||
"64498b085028fc30a58bb6a7",
|
||||
"Step 41"
|
||||
],
|
||||
[
|
||||
"5d7925341747ad42b12f8e68",
|
||||
"646d0889c6ff4baa46ac1c50",
|
||||
"Step 42"
|
||||
],
|
||||
[
|
||||
"5d792535b0b3c198ee3ed6f9",
|
||||
"646d09a07241aaab1e777080",
|
||||
"Step 43"
|
||||
],
|
||||
[
|
||||
"5d7925357a0533eb221b005d",
|
||||
"646d0a022da7bcabf3e3aca3",
|
||||
"Step 44"
|
||||
],
|
||||
[
|
||||
"5d792535591db67ee15b4106",
|
||||
"646d0d20108440acc95a6b32",
|
||||
"Step 45"
|
||||
],
|
||||
[
|
||||
"5d792535f1f7adf77de5831d",
|
||||
"646d0db5175974ad8633b71c",
|
||||
"Step 46"
|
||||
],
|
||||
[
|
||||
"5d7925353d2c505eafd50cd9",
|
||||
"646d0e4636e14eae2bb3b992",
|
||||
"Step 47"
|
||||
],
|
||||
[
|
||||
"5d79253539b5e944ba3e314c",
|
||||
"646d1980018efaaec2b1c28b",
|
||||
"Step 48"
|
||||
],
|
||||
[
|
||||
"5d792535a4f1cbff7a8b9a0b",
|
||||
"646d19fc4705e4af65c3e688",
|
||||
"Step 49"
|
||||
],
|
||||
[
|
||||
"5d792535e3304f15a8890162",
|
||||
"646d1b96dd7ea4b0061458bc",
|
||||
"Step 50"
|
||||
],
|
||||
[
|
||||
"5d792535a40ea5ac549d6804",
|
||||
"646d1cadf0d96ab0b7e12da4",
|
||||
"Step 51"
|
||||
],
|
||||
[
|
||||
"5d7925358c220e5b2998909e",
|
||||
"646d1d67f9261fb15a795588",
|
||||
"Step 52"
|
||||
],
|
||||
[
|
||||
"5d7925357729e183a49498aa",
|
||||
"646d1e531042dfb24da1f032",
|
||||
"Step 53"
|
||||
],
|
||||
[
|
||||
"5d79253555aa652afbb68086",
|
||||
"646d3141790b3cb337dd611a",
|
||||
"Step 54"
|
||||
],
|
||||
[
|
||||
"5d79253582be306d339564f6",
|
||||
"646d382c4d70ceb3dba1e830",
|
||||
"Step 55"
|
||||
],
|
||||
[
|
||||
"5d7925352047e5c54882c436",
|
||||
"646d386a685620b49db4be76",
|
||||
"Step 56"
|
||||
],
|
||||
[
|
||||
"5d79253568e441c0adf9db9f",
|
||||
"646d38c326f3c8b54023de38",
|
||||
"Step 57"
|
||||
],
|
||||
[
|
||||
"5d7925356ab117923b80c9cd",
|
||||
"646d38f906b94cb5fe6ce7de",
|
||||
"Step 58"
|
||||
],
|
||||
[
|
||||
"5d792535e54a8cd729a0d708",
|
||||
"646d3952f6af37b6a1c241c2",
|
||||
"Step 59"
|
||||
],
|
||||
[
|
||||
"5d7925353b307724a462b06b",
|
||||
"646d39c156fe94b7482c3ab6",
|
||||
"Step 60"
|
||||
],
|
||||
[
|
||||
"5d792536735f71d746ee5d99",
|
||||
"646d3b27cd3c56b875256301",
|
||||
"Step 61"
|
||||
],
|
||||
[
|
||||
"5d792536ad340d9dff2e4a96",
|
||||
"646d3bc75fe0c9b972da3323",
|
||||
"Step 62"
|
||||
],
|
||||
[
|
||||
"5d7925369614afd92d01fed5",
|
||||
"646d3c146e10b0ba222bb2a7",
|
||||
"Step 63"
|
||||
],
|
||||
[
|
||||
"5d792536504e68254fe02236",
|
||||
"646d3d037872fbbae0a8ec0e",
|
||||
"Step 64"
|
||||
],
|
||||
[
|
||||
"5d792536c8d2f0fdfad768fe",
|
||||
"646d3d65be79c8bb9c7df9ff",
|
||||
"Step 65"
|
||||
],
|
||||
[
|
||||
"5d79253639028b8ec56afcda",
|
||||
"646d3d80c3b4aebc4103618e",
|
||||
"Step 66"
|
||||
],
|
||||
[
|
||||
"5d792536834f2fd93e84944f",
|
||||
"646d3da8501e15bcd355ba1d",
|
||||
"Step 67"
|
||||
],
|
||||
[
|
||||
"5d792536ddff9ea73c90a994",
|
||||
"646d3e135ab3abbdbfe5c899",
|
||||
"Step 68"
|
||||
],
|
||||
[
|
||||
"5d7925361596f84067904f7f",
|
||||
"646d3e64b15f92be6e61704e",
|
||||
"Step 69"
|
||||
],
|
||||
[
|
||||
"5d792536dd8a4daf255488ac",
|
||||
"646d3ee7b17ae3bf48610033",
|
||||
"Step 70"
|
||||
],
|
||||
[
|
||||
"5d792536449c73004f265fb1",
|
||||
"646d3f1fd12f76c02c823bb8",
|
||||
"Step 71"
|
||||
],
|
||||
[
|
||||
"5d79253685fc69b8fe60a0d2",
|
||||
"646d3f718b5f8dc102cd528e",
|
||||
"Step 72"
|
||||
],
|
||||
[
|
||||
"5d792536dc6e3ab29525de9e",
|
||||
"646d404259f512c1a9e86ac1",
|
||||
"Step 73"
|
||||
],
|
||||
[
|
||||
"5d792536cfd0fd893c630abb",
|
||||
"646d40c543943ec250039682",
|
||||
"Step 74"
|
||||
],
|
||||
[
|
||||
"5d7925366a5ff428fb483b40",
|
||||
"646d40fe4b7b50c30c2b4cd8",
|
||||
"Step 75"
|
||||
],
|
||||
[
|
||||
"5d7925365d4035eeb2e395fd",
|
||||
"646d41e23b583fc3b8cc4579",
|
||||
"Step 76"
|
||||
],
|
||||
[
|
||||
"5d7925364c106e9aaf05a16f",
|
||||
"646d423fade4a9c4636acd13",
|
||||
"Step 77"
|
||||
],
|
||||
[
|
||||
"5d792536970cd8e819cc8a96",
|
||||
"646d42f58deb2fc52adc6611",
|
||||
"Step 78"
|
||||
],
|
||||
[
|
||||
"5d792536e33baeaa60129e0a",
|
||||
"646d43587d926bc5b6cb2e50",
|
||||
"Step 79"
|
||||
],
|
||||
[
|
||||
"5d7925379e2a488f333e2d43",
|
||||
"646d448479c8fdc8dcec868c",
|
||||
"Step 80"
|
||||
],
|
||||
[
|
||||
"5d7925379000785f6d8d9af3",
|
||||
"646d44da986f2bc9b72f5fe2",
|
||||
"Step 81"
|
||||
],
|
||||
[
|
||||
"5d79253791391b0acddd0ac5",
|
||||
"646d451c2e44afca71b67818",
|
||||
"Step 82"
|
||||
],
|
||||
[
|
||||
"5d7925373104ae5ae83f20a5",
|
||||
"646d4554721d43cb19a68bc4",
|
||||
"Step 83"
|
||||
],
|
||||
[
|
||||
"5d7925373b7127cfaeb50c26",
|
||||
"646d45b739da5ecbf830c108",
|
||||
"Step 84"
|
||||
],
|
||||
[
|
||||
"5d792537cb3a5cd6baca5e1a",
|
||||
"646d45ee725632cca2555146",
|
||||
"Step 85"
|
||||
],
|
||||
[
|
||||
"5d79253742f3313d55db981f",
|
||||
"646d4626420eeecd51f241c2",
|
||||
"Step 86"
|
||||
],
|
||||
[
|
||||
"5d7925379e0180a438ce7f95",
|
||||
"646d467c6994f4ce0dc416a4",
|
||||
"Step 87"
|
||||
],
|
||||
[
|
||||
"5d792537c80984dfa5501b96",
|
||||
"646d46c03e7d02cecb30f021",
|
||||
"Step 88"
|
||||
],
|
||||
[
|
||||
"5d7925377b54d8a76efb5657",
|
||||
"646d4717a689e1cfa232e357",
|
||||
"Step 89"
|
||||
],
|
||||
[
|
||||
"5d7925371398513549bb6395",
|
||||
"646d4769ba65f1d05ef6b634",
|
||||
"Step 90"
|
||||
],
|
||||
[
|
||||
"5d792537ea3eaf302bf2d359",
|
||||
"646d47c8f58107d10f1e5106",
|
||||
"Step 91"
|
||||
],
|
||||
[
|
||||
"5d792537533b1c7843bfd029",
|
||||
"646d4813c17b37d1e261a566",
|
||||
"Step 92"
|
||||
],
|
||||
[
|
||||
"5d792537dc0fe84345d4f19e",
|
||||
"646d486aec20f7d2a581cc36",
|
||||
"Step 93"
|
||||
],
|
||||
[
|
||||
"5d792537b6cadae0f4b0cda1",
|
||||
"646d48b936802fd34c3f05af",
|
||||
"Step 94"
|
||||
],
|
||||
[
|
||||
"5d79253770083fb730c93a93",
|
||||
"646d498c8ebc31d3f753b22e",
|
||||
"Step 95"
|
||||
],
|
||||
[
|
||||
"5d792537fef76b226b63b93b",
|
||||
"646d49bfff9079d4b38df115",
|
||||
"Step 96"
|
||||
],
|
||||
[
|
||||
"5d79253760fca25ccbbd8990",
|
||||
"646d4a07a8fb14d55cd70e09",
|
||||
"Step 97"
|
||||
],
|
||||
[
|
||||
"5d7925374321824cba309875",
|
||||
"6491d38f5b09a021c4b5d5fe",
|
||||
"Step 98"
|
||||
],
|
||||
[
|
||||
"5d7925381e8565a5c50ba7f1",
|
||||
"646d4a5b32a1cad6165df286",
|
||||
"Step 99"
|
||||
],
|
||||
[
|
||||
"5d7925383f1b77db7f1ff59e",
|
||||
"646d4a8dbc04c6d6bb0001f8",
|
||||
"Step 100"
|
||||
],
|
||||
[
|
||||
"5d792538de9fa3f298bcd5f6",
|
||||
"646d4ab9b3b4c5d74fdd2154",
|
||||
"Step 101"
|
||||
],
|
||||
[
|
||||
"5d7925385b74f69642e1fea5",
|
||||
"646d4b3d80ea98d824c8a4f9",
|
||||
"Step 102"
|
||||
],
|
||||
[
|
||||
"5d7925380ea76d55b2c97d7b",
|
||||
"Step 103"
|
||||
],
|
||||
[
|
||||
"5d792538be4fe331f1a6c008",
|
||||
"Step 104"
|
||||
],
|
||||
[
|
||||
"5d792538d169f33142175b95",
|
||||
"Step 105"
|
||||
],
|
||||
[
|
||||
"5d792538e48b5a2c6e5bbe12",
|
||||
"Step 106"
|
||||
],
|
||||
[
|
||||
"5d7925387f3e9da5ec856dbe",
|
||||
"Step 107"
|
||||
],
|
||||
[
|
||||
"5d79253824ae9b4a6e6f3108",
|
||||
"Step 108"
|
||||
],
|
||||
[
|
||||
"5d7925383f122a279f4c54ad",
|
||||
"Step 109"
|
||||
],
|
||||
[
|
||||
"5d7925387b682e962f209269",
|
||||
"Step 110"
|
||||
],
|
||||
[
|
||||
"5d792538de774217b173288e",
|
||||
"Step 111"
|
||||
],
|
||||
[
|
||||
"5d79253891d93585323d1f3c",
|
||||
"Step 112"
|
||||
],
|
||||
[
|
||||
"5d7925384e34e944ecb4612d",
|
||||
"Step 113"
|
||||
],
|
||||
[
|
||||
"5d792538631844ad0bdfb4c3",
|
||||
"Step 114"
|
||||
],
|
||||
[
|
||||
"5d792538e2a8d20cc580d481",
|
||||
"Step 115"
|
||||
],
|
||||
[
|
||||
"5d792538f5004390d6678554",
|
||||
"Step 116"
|
||||
],
|
||||
[
|
||||
"5d792539dd4fd4c96fd85f7e",
|
||||
"Step 117"
|
||||
],
|
||||
[
|
||||
"5d79253949802f8587c8bbd3",
|
||||
"Step 118"
|
||||
],
|
||||
[
|
||||
"5d7925395888767e9304c082",
|
||||
"Step 119"
|
||||
],
|
||||
[
|
||||
"5d7925393b30099e37a34668",
|
||||
"Step 120"
|
||||
],
|
||||
[
|
||||
"5d7925398157757b23730fdd",
|
||||
"Step 121"
|
||||
],
|
||||
[
|
||||
"5d792539de4b9ac14dd40409",
|
||||
"Step 122"
|
||||
],
|
||||
[
|
||||
"5d792539534f1bf991bb987f",
|
||||
"Step 123"
|
||||
],
|
||||
[
|
||||
"5d7925394089b762f93ffa52",
|
||||
"Step 124"
|
||||
],
|
||||
[
|
||||
"5d792539ec758d45a6900173",
|
||||
"Step 125"
|
||||
],
|
||||
[
|
||||
"5d7925398d525f61a9ff3a79",
|
||||
"Step 126"
|
||||
],
|
||||
[
|
||||
"5d792539a222f385c5c17d2b",
|
||||
"Step 127"
|
||||
],
|
||||
[
|
||||
"5d7925398a7184b41b12a0e0",
|
||||
"Step 128"
|
||||
],
|
||||
[
|
||||
"5d7925399afb905c34730a75",
|
||||
"Step 129"
|
||||
],
|
||||
[
|
||||
"5d792539728d1aa7788e2c9b",
|
||||
"Step 130"
|
||||
],
|
||||
[
|
||||
"5d79253939434a2724c0ec41",
|
||||
"Step 131"
|
||||
],
|
||||
[
|
||||
"5d792539b9e1d3c54d8fe94a",
|
||||
"Step 132"
|
||||
],
|
||||
[
|
||||
"5d792539b2e0bd8f9e8213e4",
|
||||
"Step 133"
|
||||
],
|
||||
[
|
||||
"5d792539239148965a1a59a5",
|
||||
"Step 134"
|
||||
],
|
||||
[
|
||||
"5d792539e1446045d0df6d28",
|
||||
"Step 135"
|
||||
],
|
||||
[
|
||||
"5d79253a2febbb77098730b9",
|
||||
"Step 136"
|
||||
],
|
||||
[
|
||||
"5d79253a98bd9fdf7ce68d0a",
|
||||
"Step 137"
|
||||
],
|
||||
[
|
||||
"5d79253a1e9abf29de64c177",
|
||||
"Step 138"
|
||||
],
|
||||
[
|
||||
"5d79253a8b29d78984369e4b",
|
||||
"Step 139"
|
||||
],
|
||||
[
|
||||
"5d79253ad297a31cbe073718",
|
||||
"Step 140"
|
||||
],
|
||||
[
|
||||
"5dc10b8b93704f41d279eb5b",
|
||||
"Step 141"
|
||||
]
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
---
|
||||
id: 642db8c409d9991d0b3b2f0d
|
||||
title: Step 1
|
||||
challengeType: 0
|
||||
dashedName: step-1
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your project starts with a basic HTML container and some corresponding CSS. Your first task will be to programmatically generate the cells for your spreadsheet.
|
||||
|
||||
The global `window` object represents the browser window (or tab). It has an `onload` property which allows you to define behavior when the window has loaded the entire page, including stylesheets and scripts.
|
||||
|
||||
Start by setting the `onload` property of `window` to an arrow function with no parameters. In the function, declare a `container` variable and assign it the value of getting the element by the `id` of `container`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should access the `onload` property of the `window` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload/);
|
||||
```
|
||||
|
||||
You should set the `onload` property to a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(window.onload);
|
||||
```
|
||||
|
||||
You should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(/);
|
||||
```
|
||||
|
||||
Your `onload` function should not take any parameters.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)/);
|
||||
```
|
||||
|
||||
You should declare a `container` variable in your `onload` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*(?:let|var|const)\s+container/);
|
||||
```
|
||||
|
||||
Your `container` variable should be declared with `const`.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container/);
|
||||
```
|
||||
|
||||
You should use `document.getElementById()`
|
||||
|
||||
```js
|
||||
assert.match(code, /document\.getElementById\(/);
|
||||
```
|
||||
|
||||
You should get the element with the `id` of `container`.
|
||||
|
||||
```js
|
||||
assert.match(code, /document\.getElementById\(\s*('|"|`)container\1\s*\)/);
|
||||
```
|
||||
|
||||
You should assign the `#container` element to `container`.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: 642dccb78549c9285835ebc2
|
||||
title: Step 2
|
||||
challengeType: 0
|
||||
dashedName: step-2
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Functions are ideal for reusable logic. When a function itself needs to reuse logic, you can declare a nested function to handle that logic. Here is an example of a nested function:
|
||||
|
||||
```js
|
||||
const outer = () => {
|
||||
const inner = () => {
|
||||
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
Declare a nested `createLabel` function using arrow syntax. It should take a `name` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `createLabel` variable in your `onload` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*(?:const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?)?\s*(?:let|var|const)\s*createLabel/);
|
||||
```
|
||||
|
||||
Your `createLabel` variable should be declared after your `container` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?\s*(?:let|var|const)\s*createLabel/);
|
||||
```
|
||||
|
||||
Your `createLabel` variable should be declared with `const`.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?\s*const\s*createLabel/);
|
||||
```
|
||||
|
||||
Your `createLabel` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?\s*const\s*createLabel\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `createLabel` function should have a `name` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?\s*const\s*createLabel\s*=\s*\(?\s*name\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `createLabel` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /window\.onload\s*=\s*\(\s*\)\s*=>\s*\{\s*const\s+container\s*=\s*document\.getElementById\(\s*('|"|`)container\1\s*\);?\s*const\s*createLabel\s*=\s*\(?\s*name\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: 642ddfdea4200e313f80a4b6
|
||||
title: Step 3
|
||||
challengeType: 0
|
||||
dashedName: step-3
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remember that the `document` object has a `.createElement()` method which allows you to dynamically create new HTML elements.
|
||||
|
||||
In your `createLabel` function, declare a `label` variable and assign it a new `div` element.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `label` variable in your `createLabel` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*createLabel\s*=\s*\(?\s*name\s*\)?\s*=>\s*\{\s*(?:const|let|var)\s*label/);
|
||||
```
|
||||
|
||||
Your `label` variable should be declared with `const`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*createLabel\s*=\s*\(?\s*name\s*\)?\s*=>\s*\{\s*const\s*label/);
|
||||
```
|
||||
|
||||
You should use the `.createElement()` method of the `document` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /document\.createElement\(/);
|
||||
```
|
||||
|
||||
You should pass the string `div` to the `.createElement()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /document\.createElement\(\s*('|"|`)div\1\s*\)/);
|
||||
```
|
||||
|
||||
You should assign your new `div` element to `label`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*label\s*=\s*document\.createElement\(\s*('|"|`)div\1\s*\)/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 642def66e6a60432c9a0371e
|
||||
title: Step 4
|
||||
challengeType: 0
|
||||
dashedName: step-4
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Set the `className` of the `label` element to `label`, and set the `textContent` to the `name` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should access the `className` property of the `label` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /label\.className/);
|
||||
```
|
||||
|
||||
You should set the `className` property to the string `label`.
|
||||
|
||||
```js
|
||||
assert.match(code, /label\.className\s*=\s*('|"|`)label\1/);
|
||||
```
|
||||
|
||||
You should access the `textContent` property of the `label` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /label\.textContent/);
|
||||
```
|
||||
|
||||
You should set the `textContent` property to the value of `name`.
|
||||
|
||||
```js
|
||||
assert.match(code, /label\.textContent\s*=\s*name/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,81 @@
|
||||
---
|
||||
id: 642df32c0c2db433d8b46d46
|
||||
title: Step 5
|
||||
challengeType: 0
|
||||
dashedName: step-5
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Finally, use the `.appendChild()` method to add your `label` element to the `container` element.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should access the `.appendChild()` method of the `container` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /container\.appendChild\(/);
|
||||
```
|
||||
|
||||
You should pass your `label` element to the `.appendChild()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /container\.appendChild\(\s*label\s*\)/);
|
||||
```
|
||||
|
||||
You should append `label` after setting the attributes.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*label\s*=\s*document.createElement\(\s*('|"|`)div\1\s*\);?\s*label\.className\s*=\s*('|"|`)label\2;?\s*label\.textContent\s*=\s*name;?\s*container\.appendChild\(\s*label\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,121 @@
|
||||
---
|
||||
id: 642df9df4b5216350de7b0d2
|
||||
title: Step 6
|
||||
challengeType: 0
|
||||
dashedName: step-6
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You will need a function to generate a range of numbers.
|
||||
|
||||
Declare an empty `range` function which takes a `start` and `end` parameter. Use the `Array()` constructor and implicitly return an empty array.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `range` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|var|const)\s*range/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `range` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range/);
|
||||
```
|
||||
|
||||
Your `range` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(range);
|
||||
```
|
||||
|
||||
Your `range` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(.*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `range` function should take a `start` parameter first.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start/)
|
||||
```
|
||||
|
||||
Your `range` function should take an `end` parameter second.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)/);
|
||||
```
|
||||
|
||||
Your `range` function should use an implicit return. Remember that this means you will not use curly brackets.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*{/);
|
||||
```
|
||||
|
||||
Your `range` function should use the `Array()` constructor. Primitive constructors do not need the `new` keyword.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(/);
|
||||
```
|
||||
|
||||
You should not pass anything to the `Array()` constructor.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 642dfb07e7fa6736251541c8
|
||||
title: Step 7
|
||||
challengeType: 0
|
||||
dashedName: step-7
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your array will need to be the size of the range. You can calculate this by finding the difference between `end` and `start`, and adding `1` to the result.
|
||||
|
||||
Pass this calculation as the argument for your `Array()` constructor.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should subtract `start` from `end`.
|
||||
|
||||
```js
|
||||
assert.match(code, /end\s*-\s*start/);
|
||||
```
|
||||
|
||||
You should add `1` to your `end - start` calculation.
|
||||
|
||||
```js
|
||||
assert.match(code, /end\s*-\s*start\s*\+\s*1/);
|
||||
```
|
||||
|
||||
You should pass your calculation to the `Array()` constructor.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const range = (start, end) => Array();
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,85 @@
|
||||
---
|
||||
id: 642e0011c45c893845842058
|
||||
title: Step 8
|
||||
challengeType: 0
|
||||
dashedName: step-8
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `Array()` constructor has a `.fill()` method which can be used to fill an array with a value. You can use this to fill your array with the `start` value.
|
||||
|
||||
Chain the `.fill()` method to your `Array()` constructor, and pass it the `start` value.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the `.fill()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /\.fill\(/);
|
||||
```
|
||||
|
||||
You should call the `.fill()` method on your `Array()` constructor.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\)\.fill\(/);
|
||||
```
|
||||
|
||||
You should pass `start` to the `.fill()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const range = (start, end) => Array(end - start + 1);
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: 642e004130958c3975aa3a4a
|
||||
title: Step 9
|
||||
challengeType: 0
|
||||
dashedName: step-9
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Currently your `range` function returns an array with the correct length, but all of the values are the value of `start`. To fix this, chain the `.map()` method to your `.fill()` method.
|
||||
|
||||
Pass the `.map()` method a callback which takes `element` and `index` as parameters and returns the sum of those parameters.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the `.map()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /\.map\(/);
|
||||
```
|
||||
|
||||
You should chain the `.map()` method to your `.fill()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(/);
|
||||
```
|
||||
|
||||
You should pass a callback function to `.map()` using arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(\s*\(.*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should take `element` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(\s*\(\s*element/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should take `index` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(\s*\(\s*element\s*,\s*index\s*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(\s*\(\s*element\s*,\s*index\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should implicitly return the sum of `element` and `index`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*range\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*Array\(\s*end\s*-\s*start\s*\+\s*1\s*\).fill\(\s*start\s*\)\.map\(\s*\(\s*element\s*,\s*index\s*\)\s*=>\s*(element\s*\+\s*index|index\s*\+\s*element)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const range = (start, end) => Array(end - start + 1).fill(start);
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
id: 642e02be7845f13b014cd2b0
|
||||
title: Step 10
|
||||
challengeType: 0
|
||||
dashedName: step-10
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you have a `range` function, you can use it to create a range of letters as well.
|
||||
|
||||
Declare a `charRange` function using `const` and arrow syntax. It should take a `start` and `end` parameter. The function should implicitly return the result of calling `range()` with `start` and `end` as the arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `charRange` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s*charRange/);
|
||||
```
|
||||
|
||||
Your `charRange` variable should be declared with `const`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange/);
|
||||
```
|
||||
|
||||
Your `charRange` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(charRange);
|
||||
```
|
||||
|
||||
Your `charRange` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(.*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `charRange` function should take `start` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start/);
|
||||
```
|
||||
|
||||
Your `charRange` function should take `end` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)/);
|
||||
```
|
||||
|
||||
Your `charRange` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*{/);
|
||||
```
|
||||
|
||||
Your `charRange` function should call your `range` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(/);
|
||||
```
|
||||
|
||||
You should pass `start` and `end` as the arguments to your `range` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\s*,\s*end\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: 6434552bcc0a951a0a99df3b
|
||||
title: Step 11
|
||||
challengeType: 0
|
||||
dashedName: step-11
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `range` function expects numbers, but your `start` and `end` values will be strings (specifically, they will be single characters such as `A`).
|
||||
|
||||
Convert your `start` and `end` values in your `range()` call to numbers by using the `.charCodeAt()` method on them, passing the number `0` as the argument to that method.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the `.charCodeAt()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /\.charCodeAt\(/);
|
||||
```
|
||||
|
||||
You should call the `.charCodeAt()` method on `start`.
|
||||
|
||||
```js
|
||||
assert.match(code, /start\.charCodeAt\(/);
|
||||
```
|
||||
|
||||
You should pass `0` to the `.charCodeAt()` method of `start`.
|
||||
|
||||
```js
|
||||
assert.match(code, /start\.charCodeAt\(\s*0\s*\)/);
|
||||
```
|
||||
|
||||
You should call the `.charCodeAt()` method on `end`.
|
||||
|
||||
```js
|
||||
assert.match(code, /end\.charCodeAt\(/);
|
||||
```
|
||||
|
||||
You should pass `0` to the `.charCodeAt()` method of `end`.
|
||||
|
||||
```js
|
||||
assert.match(code, /end\.charCodeAt\(\s*0\s*\)/);
|
||||
```
|
||||
|
||||
You should use the `.charCodeAt()` methods directly in your `range` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
--fcc-editable-region--
|
||||
const charRange = (start, end) => range(start, end);
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,110 @@
|
||||
---
|
||||
id: 64345b810a6e481e5e326849
|
||||
title: Step 12
|
||||
challengeType: 0
|
||||
dashedName: step-12
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`range()` will return an array of numbers, which you need to convert back into characters. Chain the `.map()` method to your `range()` call.
|
||||
|
||||
Pass a callback function that takes `code` as the parameter and implicitly returns the value of passing `code` to the `String.fromCharCode()` method.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the `.map()` method.
|
||||
|
||||
```js
|
||||
assert.lengthOf(code.match(/\.map\(/g), 2);
|
||||
```
|
||||
|
||||
You should chain the `.map()` method to your `range` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\)\.map\(/);
|
||||
```
|
||||
|
||||
You should use arrow syntax for the `.map()` callback.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\).map\(\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should take a `code` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\).map\(\s*\(?\s*code\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\).map\(\s*\(?\s*code\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `.map()` callback should return the result of calling `String.fromCharCode()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\).map\(\s*\(?\s*code\s*\)?\s*=>\s*String\.fromCharCode\(/);
|
||||
```
|
||||
|
||||
You should pass the variable `code` to `String.fromCharCode()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*charRange\s*=\s*\(\s*start\s*,\s*end\s*\)\s*=>\s*range\(\s*start\.charCodeAt\(\s*0\s*\)\s*,\s*end\.charCodeAt\(\s*0\s*\)\s*\).map\(\s*\(?\s*code\s*\)?\s*=>\s*String\.fromCharCode\(\s*code\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
--fcc-editable-region--
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0));
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: 64345c560591891f64976f7a
|
||||
title: Step 13
|
||||
challengeType: 0
|
||||
dashedName: step-13
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that your helper functions are complete, back in your `onload` event handler you should declare a `letters` variable. Assign it the result of calling `charRange()` with the letters `A` and `J` as arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `letters` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s*letters/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `letters` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*letters/);
|
||||
```
|
||||
|
||||
You should assign a `charRange()` call to your `letters` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*letters\s*=\s*charRange\(/);
|
||||
```
|
||||
|
||||
You should pass `A` as the first argument to your `charRange()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*letters\s*=\s*charRange\(\s*('|"|`)A\1/);
|
||||
```
|
||||
|
||||
You should pass `J` as the second argument to your `charRange()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*letters\s*=\s*charRange\(\s*('|"|`)A\1\s*,\s*('|"|`)J\2\s*\)/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,88 @@
|
||||
---
|
||||
id: 64347464f78cd9209545f35c
|
||||
title: Step 14
|
||||
challengeType: 0
|
||||
dashedName: step-14
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now call the `.forEach()` method of your `letters` array, and pass your `createLabel` function reference as the callback.
|
||||
|
||||
You should see some letters appear across the top of your spreadsheet.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call the `.forEach()` method on your `letters` array.
|
||||
|
||||
```js
|
||||
assert.match(code, /letters\.forEach\(/);
|
||||
```
|
||||
|
||||
You should pass your `createLabel` function reference to the `.forEach()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /letters\.forEach\(\s*createLabel\s*\)/);
|
||||
```
|
||||
|
||||
You should not pass a `createLabel` function call.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /letters\.forEach\(\s*createLabel\(\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,107 @@
|
||||
---
|
||||
id: 6434750c53db16218f41e6e1
|
||||
title: Step 15
|
||||
challengeType: 0
|
||||
dashedName: step-15
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Remember that `range()` returns an array, so you can chain array methods directly to the function call.
|
||||
|
||||
Call `range()` with `1` and `99` as the arguments, and chain the `.forEach()` method. Pass the `.forEach()` method an empty callback which takes `number` as the parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call your `range()` function.
|
||||
|
||||
```js
|
||||
assert.lengthOf(code.match(/range\(/g), 2);
|
||||
```
|
||||
|
||||
You should pass `1` as the first argument to your `range()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1/);
|
||||
```
|
||||
|
||||
You should pass `99` as the second argument to your `range()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)/);
|
||||
```
|
||||
|
||||
You should chain the `.forEach()` method to your `range()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)\.forEach\(/);
|
||||
```
|
||||
|
||||
You should pass a callback function to `.forEach()` using arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)\.forEach\(\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should have `number` as the only parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)\.forEach\(\s*\(?\s*number\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: 6434759f78ec812264ff8f34
|
||||
title: Step 16
|
||||
challengeType: 0
|
||||
dashedName: step-16
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your callback, call `createLabel()` and pass `number` as the argument. You should see some numbers appear in your spreadsheet.
|
||||
|
||||
Then call the `.forEach()` method on your `letters` array. Pass an empty callback function which takes a `letter` argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call your `createLabel()` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)\.forEach\(\s*\(?\s*number\s*\)?\s*=>\s*\{\s*createLabel\(/);
|
||||
```
|
||||
|
||||
You should pass `number` to your `createLabel()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /range\(\s*1\s*,\s*99\s*\)\.forEach\(\s*\(?\s*number\s*\)?\s*=>\s*\{\s*createLabel\(/)
|
||||
```
|
||||
|
||||
You should call the `.forEach()` method on your `letters` array.
|
||||
|
||||
```js
|
||||
assert.lengthOf(code.match(/letters\.forEach\(/g), 2)
|
||||
```
|
||||
|
||||
You should pass a callback function with arrow syntax to your `.forEach()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /letters\.forEach\(\s*\(?.*\)?\s*=>\s*\{/)
|
||||
```
|
||||
|
||||
Your callback function should have a `letter` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /letters\.forEach\(\s*\(?\s*letter\s*\)?\s*=>\s*\{/)
|
||||
```
|
||||
|
||||
Your callback function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /letters\.forEach\(\s*\(?\s*letter\s*\)?\s*=>\s*\{\s*\}/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
--fcc-editable-region--
|
||||
range(1, 99).forEach(number => {
|
||||
|
||||
})
|
||||
--fcc-editable-region--
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,142 @@
|
||||
---
|
||||
id: 643475e13dc727231acd0f72
|
||||
title: Step 17
|
||||
challengeType: 0
|
||||
dashedName: step-17
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now in your nested `.forEach()` call, declare an `input` variable. Use the `.createElement()` method of the `document` object to create an `input` element. Set the `type` attribute to `text` and the `id` attribute to `letter + number`.
|
||||
|
||||
For accessibility, set the `aria-label` attribute to the same value as the `id` attribute.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `input` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:var|let|const)\s*input/)
|
||||
```
|
||||
|
||||
You should use `const` to declare your `input` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*input/)
|
||||
```
|
||||
|
||||
You should call the `.createElement()` method of the `document` object.
|
||||
|
||||
```js
|
||||
assert.lengthOf(code.match(/document\.createElement\(/g), 2)
|
||||
```
|
||||
|
||||
You should pass the string `input` to the `.createElement()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /document\.createElement\(\s*('|"|`)input\1\s*\)/)
|
||||
```
|
||||
|
||||
You should assign your new `input` element to your `input` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*input\s*=\s*document\.createElement\(\s*('|"|`)input\1\s*\)/)
|
||||
```
|
||||
|
||||
You should access the `type` property of your `input` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.type/);
|
||||
```
|
||||
|
||||
You should set the `type` attribute of your `input` element to `text`.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.type\s*=\s*('|"|`)text\1/)
|
||||
```
|
||||
|
||||
You should access the `id` property of your `input` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.id/);
|
||||
```
|
||||
|
||||
You should set the `id` attribute of your `input` element to `letter + number`.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.id\s*=\s*letter\s\+\snumber/)
|
||||
```
|
||||
|
||||
You should access the `ariaLabel` property of your `input` element.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.ariaLabel/);
|
||||
```
|
||||
|
||||
You should set the `aria-label` attribute of your `input` element to `letter + number`.
|
||||
|
||||
```js
|
||||
assert.match(code, /input\.ariaLabel\s*=\s*letter\s\+\snumber/)
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
--fcc-editable-region--
|
||||
letters.forEach(letter => {
|
||||
|
||||
})
|
||||
--fcc-editable-region--
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,92 @@
|
||||
---
|
||||
id: 643498328cb52026123e2b91
|
||||
title: Step 18
|
||||
challengeType: 0
|
||||
dashedName: step-18
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Append the `input` element to your `container` element as a child.
|
||||
|
||||
You should now be able to see the cells of your spreadsheet.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call the `.appendChild()` method on your `container` element.
|
||||
|
||||
```js
|
||||
assert.lengthOf(code.match(/container\.appendChild\(/g), 2);
|
||||
```
|
||||
|
||||
You should pass your `input` element to the `.appendChild()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /container\.appendChild\(\s*input\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
--fcc-editable-region--
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
|
||||
})
|
||||
--fcc-editable-region--
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,131 @@
|
||||
---
|
||||
id: 643498755d54c6279ba09078
|
||||
title: Step 19
|
||||
challengeType: 0
|
||||
dashedName: step-19
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Most spreadsheet programs include built-in functions for calculation.
|
||||
|
||||
Declare a `sum` function that takes a `nums` parameter, which will be an array of numbers. It should return the result of calling `reduce` on the array to sum all of the numbers.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `sum` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s*sum/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `sum` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*sum/);
|
||||
```
|
||||
|
||||
Your `sum` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(sum);
|
||||
```
|
||||
|
||||
Your `sum` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*sum\s*=\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `sum` function should have a `nums` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*sum\s*=\s*\(?\s*nums\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `sum` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*sum\s*=\s*\(?\s*nums\s*\)?\s*=>\s*{/);
|
||||
```
|
||||
|
||||
Your `sum` function should return the result of calling `.reduce()` on `nums`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*sum\s*=\(?\s*nums\s*\)?\s*=>\s*nums\.reduce\(/);
|
||||
```
|
||||
|
||||
Your `sum` function should return the sum of all numbers in `nums`.
|
||||
|
||||
```js
|
||||
const numbers = [1, 2, 3, 4, 5];
|
||||
assert.equal(sum(numbers), 15);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
id: 6437124c4c03dd4c8fb35d56
|
||||
title: Step 20
|
||||
challengeType: 0
|
||||
dashedName: step-20
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare an `isEven` function, which takes a `num` parameter and returns `true` if the number is even, and `false` otherwise. Use the modulo operator `%` to determine if a number is even or odd.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `isEven` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s*isEven/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `isEven` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*isEven/);
|
||||
```
|
||||
|
||||
Your `isEven` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(isEven);
|
||||
```
|
||||
|
||||
Your `isEven` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*isEven\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `isEven` function should have a `num` argument.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*isEven\s*=\s*\(?\s*num\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `isEven` function should use the modulo operator `%`.
|
||||
|
||||
```js
|
||||
assert.match(isEven.toString(), /%/);
|
||||
```
|
||||
|
||||
Your `isEven` function should return `true` for even numbers.
|
||||
|
||||
```js
|
||||
assert.isTrue(isEven(2));
|
||||
assert.isTrue(isEven(1000));
|
||||
assert.isTrue(isEven(42));
|
||||
```
|
||||
|
||||
Your `isEven` function should return `false` for odd numbers.
|
||||
|
||||
```js
|
||||
assert.isFalse(isEven(1));
|
||||
assert.isFalse(isEven(333));
|
||||
assert.isFalse(isEven(777777777));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 6437133052eaf04d7300e622
|
||||
title: Step 21
|
||||
challengeType: 0
|
||||
dashedName: step-21
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare an `average` function which takes an array of numbers as the `nums` parameter. It should return the average of all the numbers in the array.
|
||||
|
||||
The average can be calculated by dividing the sum of all the numbers in the array by the length of the array. Remember that you have a `sum` function you can use.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `average` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s+average/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `average` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+average/);
|
||||
```
|
||||
|
||||
Your `average` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(average);
|
||||
```
|
||||
|
||||
Your `average` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+average\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `average` function should have a `nums` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+average\s*=\s*\(?\s*nums\s*\)?/);
|
||||
```
|
||||
|
||||
Your `average` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+average\s*=\s*\(?\s*nums\s*\)?\s*=>\s*{/);
|
||||
```
|
||||
|
||||
Your `average` function should return the average value of the `nums` array.
|
||||
|
||||
```js
|
||||
assert.equal(average([1,2,3]), 2);
|
||||
assert.equal(average([1,2,3,4,5]), 3);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,154 @@
|
||||
---
|
||||
id: 643715013330824ecaa70442
|
||||
title: Step 22
|
||||
challengeType: 0
|
||||
dashedName: step-22
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your next function will calculate the median value of an array of numbers. Start by declaring a `median` arrow function that takes a `nums` parameter.
|
||||
|
||||
In the function, declare a `sorted` variable and assign it the value of sorting a copy of the `nums` array.
|
||||
|
||||
You should use the `slice()` method for creating a shallow copy of the array.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `median` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s+median/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `median` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median/);
|
||||
```
|
||||
|
||||
Your `median` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(median);
|
||||
```
|
||||
|
||||
Your `median` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?/);
|
||||
```
|
||||
|
||||
Your `median` function should have a `nums` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?/);
|
||||
```
|
||||
|
||||
Your `median` function should not use an implicit return.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `median` function should have a `sorted` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{\s*(?:let|var|const)\s+sorted/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `sorted` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{\s*const\s+sorted/);
|
||||
```
|
||||
|
||||
You should use `.slice()` to assign a copy of the `nums` array to `sorted`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)/);
|
||||
```
|
||||
|
||||
You should chain the `.sort()` method to your `.slice()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(/);
|
||||
```
|
||||
|
||||
You should pass a callback function to your `sort` method to accurately sort the numbers in ascending order. Use an implicit return for clarity.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*\(?\s*nums\s*\)?\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
id: 64496d1e5af8c0148fbef96d
|
||||
title: Step 23
|
||||
challengeType: 0
|
||||
dashedName: step-23
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now declare a `length` variable and assign it the length of your sorted array, and a `middle` variable that has the value of the length divided by `2`, subtracted by `1`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `length` variable after your `sorted` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*(?:var|let|const)\s+length/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `length` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length/);
|
||||
```
|
||||
|
||||
You should assign the length of the `sorted` array to your `length` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?/);
|
||||
```
|
||||
|
||||
You should declare a `middle` variable after your `length` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*(?:var|let|const)\s+middle/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `middle` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle/);
|
||||
```
|
||||
|
||||
You should assign `middle` the value of dividing your `length` variable by `2`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2/);
|
||||
```
|
||||
|
||||
You should subtract `1` from your `length / 2` calculation.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
--fcc-editable-region--
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: 64496d80bc174a158c973080
|
||||
title: Step 24
|
||||
challengeType: 0
|
||||
dashedName: step-24
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Using ternary syntax, check if `length` is even using your `isEven` function. If it is, return the average of the number at the `middle` index and the number after that. If it's odd, return the number at the `middle` index – you'll need to round the `middle` value up.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should use the `return` keyword.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return/);
|
||||
```
|
||||
|
||||
You should call your `isEven()` function after your `return` keyword.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(/);
|
||||
```
|
||||
|
||||
You should pass your `length` variable to your `isEven()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)/);
|
||||
```
|
||||
|
||||
You should use ternary syntax to check the truthiness of your `isEven()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?/);
|
||||
```
|
||||
|
||||
If the ternary is truthy, you should call your `average()` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?\s*average\(/);
|
||||
```
|
||||
|
||||
You should pass an array to your `average()` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?\s*average\(\s*\[/);
|
||||
```
|
||||
|
||||
The first element of the array passed to `average()` should be the element at the `middle` index of your `sorted` array.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?\s*average\(\s*\[\s*sorted\[\s*middle\s*\]/);
|
||||
```
|
||||
|
||||
The first element of the array passed to `average()` should be the element at the `middle + 1` index of your `sorted` array.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?\s*average\(\s*\[\s*sorted\[\s*middle\s*\]\s*,\s*sorted\[\s*middle\s*\+\s*1\s*\]\s*\]\)/);
|
||||
```
|
||||
|
||||
If the ternary is false, you should return the value of `sorted` at the `middle` index. Use `Math.ceil()` to round the `middle` value up.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+median\s*=\s*nums\s*=>\s*\{\s*const\s+sorted\s*=\s*nums\.slice\(\s*\)\.sort\(\s*\(\s*a\s*,\s*b\s*\)\s*=>\s*a\s*-\s*b\s*\)\s*\s*;?\s*const\s+length\s*=\s*sorted\.length;?\s*const\s+middle\s*=\s*length\s*\/\s*2\s*-\s*1\s*;?\s*return\s+isEven\(\s*length\s*\)\s*\?\s*average\(\s*\[\s*sorted\[\s*middle\s*\]\s*,\s*sorted\[\s*middle\s*\+\s*1\s*\]\s*\]\s*\)\s*:\s*sorted\[\s*Math\.ceil\(\s*middle\s*\)\s*\];?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
--fcc-editable-region--
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
id: 64496df724dd3716a71fe971
|
||||
title: Step 25
|
||||
challengeType: 0
|
||||
dashedName: step-25
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
To keep track of all of your spreadsheet's functions, delcare a `spreadsheetFunctions` object. Using destructuring syntax, set `sum`, `average`, and `median` as properties and values on the `spreadsheetFunctions` object.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `spreadsheetFunctions` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:const|let|var)\s+spreadsheetFunctions/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `spreadsheetFunctions` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+spreadsheetFunctions/);
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` variable should be an object.
|
||||
|
||||
```js
|
||||
assert.isObject(spreadsheetFunctions);
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `sum` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "sum");
|
||||
```
|
||||
|
||||
Your `sum` property should be your `sum` function.
|
||||
|
||||
```js
|
||||
assert.equal(spreadsheetFunctions?.sum, sum);
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` object should have an `average` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "average");
|
||||
```
|
||||
|
||||
Your `average` property should be your `average` function.
|
||||
|
||||
```js
|
||||
assert.equal(spreadsheetFunctions?.average, average);
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `median` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "median");
|
||||
```
|
||||
|
||||
Your `median` property should be your `median` function.
|
||||
|
||||
```js
|
||||
assert.equal(spreadsheetFunctions?.median, median);
|
||||
```
|
||||
|
||||
You should use destructuring syntax to assign your properties.
|
||||
|
||||
```js
|
||||
const objectText = code.replace(/.*const\s*spreadsheetFunctions\s*=\s*\{([^}]*)}.*/s, "$1");
|
||||
assert.include(objectText, "sum");
|
||||
assert.include(objectText, "average");
|
||||
assert.include(objectText, "median");
|
||||
assert.notInclude(objectText, ":");
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
id: 64496e9c6d7a2e189948e441
|
||||
title: Step 26
|
||||
challengeType: 0
|
||||
dashedName: step-26
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you can start using your spreadsheet functions. Begin by declaring an `update` arrow function. It should take an `event` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `update` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s+update/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `update` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update/);
|
||||
```
|
||||
|
||||
Your `update` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(update);
|
||||
```
|
||||
|
||||
Your `update` function should take an `event` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `update` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
id: 6449749d20436c1f1dfadcf2
|
||||
title: Step 27
|
||||
challengeType: 0
|
||||
dashedName: step-27
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `window.onload` function, you need to tell your `input` elements to call the `update` function when the value changes. You can do this by directly setting the `onchange` property.
|
||||
|
||||
Set the `onchange` property to be a reference to your `update` function.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `window.onload` function should access the `onchange` property of the `input` element.
|
||||
|
||||
```js
|
||||
assert.match(window.onload.toString(), /input\.onchange/);
|
||||
```
|
||||
|
||||
Your `window.onload` function should set the `onchange` property to `update`.
|
||||
|
||||
```js
|
||||
assert.match(window.onload.toString(), /input\.onchange\s*=\s*update/);
|
||||
```
|
||||
|
||||
Your `window.onload` function should not call your `update` function.
|
||||
|
||||
```js
|
||||
assert.notMatch(window.onload.toString(), /update\(\s*\)/);
|
||||
```
|
||||
|
||||
Your `input` elements should all have your `update` function as the `onchange` property.
|
||||
|
||||
```js
|
||||
const inputs = document.querySelectorAll('input');
|
||||
inputs.forEach(input => {
|
||||
assert.property(input, 'onchange');
|
||||
assert.equal(input.onchange, update);
|
||||
})
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
id: 6449755666005520330cec5b
|
||||
title: Step 28
|
||||
challengeType: 0
|
||||
dashedName: step-28
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Since your `update` event is running as a `change` event listener, the `event` argument will be a change event.
|
||||
|
||||
The `target` property of the change event represents the element that changed. Assign the `target` property to a new variable called `element`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `element` variable in your `update` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*(?:var|let|const)\s+element/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `element` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element/);
|
||||
```
|
||||
|
||||
You should assign the `target` property of the `event` argument to your `element` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,147 @@
|
||||
---
|
||||
id: 64497da4062602213ecf32e7
|
||||
title: Step 29
|
||||
challengeType: 0
|
||||
dashedName: step-29
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Because the `change` event is triggering on an `input` element, the element will have a `value` property that represents the current value of the input.
|
||||
|
||||
Assign the `value` property of `element` to a new variable called `value`, and use `.replace()` to remove all whitespace.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `value` variable after your `element` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*(?:const|let|var)\s+value/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `value` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value/);
|
||||
```
|
||||
|
||||
You should assign the `value` property of `element` to your `value` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value/);
|
||||
```
|
||||
|
||||
You should call the `.replace()` method on the `value` property of the `element`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(/);
|
||||
```
|
||||
|
||||
You should pass a regular expression to match whitespace to your `.replace()` method. Use the `\s` character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\//);
|
||||
```
|
||||
|
||||
You should make your regular expression global.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g/);
|
||||
```
|
||||
|
||||
You should pass an empty string as your second argument to the `.replace()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,122 @@
|
||||
---
|
||||
id: 64497de936a2f322327e5c58
|
||||
title: Step 30
|
||||
challengeType: 0
|
||||
dashedName: step-30
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you need to check if the `value` does not include the `id` of the element. Create an `if` condition to do so.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should create an `if` block.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(/);
|
||||
```
|
||||
|
||||
Your `if` condition should check if `value` includes the `id` of the `element`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*!value\.includes\(\s*element\.id\s*\)/);
|
||||
```
|
||||
|
||||
Your `if` block should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*!value\.includes\(\s*element\.id\s*\)\s*\)\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,114 @@
|
||||
---
|
||||
id: 64497e0e5e5a2c2329785af4
|
||||
title: Step 31
|
||||
challengeType: 0
|
||||
dashedName: step-31
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Spreadsheet software typically uses `=` at the beginning of a cell to indicate a calculation should be used, and spreadsheet functions should be evaluated.
|
||||
|
||||
Update your `if` condition to also check if the first character of `value` is `=`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `if` condition should also check if the first character of `value` is `=`. You may use `[0]`, `.startsWith()`, or `.charAt(0)`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id)) {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
id: 64497e764135bd24b7960dd3
|
||||
title: Step 32
|
||||
challengeType: 0
|
||||
dashedName: step-32
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In order to run your spreadsheet functions, you need to be able to parse and evaluate the input string. This is a great time to use another function.
|
||||
|
||||
Declare an `evalFormula` arrow function which takes the parameters `x` and `cells`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `evalFormula` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:let|const|var)\s*evalFormula/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `evalFormula` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula/);
|
||||
```
|
||||
|
||||
Your `evalFormula` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(evalFormula);
|
||||
```
|
||||
|
||||
Your `evalFormula` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(/);
|
||||
```
|
||||
|
||||
Your `evalFormula` function should have `x` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x/);
|
||||
```
|
||||
|
||||
Your `evalFormula` function should have `cells` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `evalFormula` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,166 @@
|
||||
---
|
||||
id: 6449842c6f6c84261075e4c9
|
||||
title: Step 33
|
||||
challengeType: 0
|
||||
dashedName: step-33
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `evalFormula`, declare an `idToText` arrow function which takes an `id` parameter.
|
||||
|
||||
Your `idToText` function should return the result of calling `.find()` on the `cells` array with a callback function that takes an `cell` parameter and returns `cell.id === id`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `idToText` variable in your `evalFormula` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*(?:const|let|var)\s+idToText/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `idToText` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText/);
|
||||
```
|
||||
|
||||
Your `idToText` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `idToText` function should have an `id` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
You should assign `idToText` the result of calling the `.find()` method on your `cells` array.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(/);
|
||||
```
|
||||
|
||||
You should pass a callback function to your `.find()` method. Use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should have a `cell` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should return whether `cell.id` is strictly equal to `id`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,116 @@
|
||||
---
|
||||
id: 64498473a17adc26ef0ecc2d
|
||||
title: Step 34
|
||||
challengeType: 0
|
||||
dashedName: step-34
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `idToText` function currently returns an `input` element. Update it to return the `value` of that `input` element.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should return the `value` property of the return value of the `.find()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
id: 6449849b78f43527be1e8a98
|
||||
title: Step 35
|
||||
challengeType: 0
|
||||
dashedName: step-35
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You need to be able to match cell ranges in a formula. Cell ranges can look like `A1:B12` or `A3:A25`. You can use a regular expression to match these patterns.
|
||||
|
||||
Start by declaring a `rangeRegex` variable and assign it a regular expression that matches `A` through `J` (the range of columns in your spreadsheet). Use a capture group with a character class to achieve this.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `rangeRegex` variable after your `idToText` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*(?:var|let|const)\s+rangeRegex/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `rangeRegex` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` variable should be a regular expression.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/.*\/;?/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` should use a capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(.*\)\/;?/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` should use a character class in the capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[.*\]\)\/;?/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` should use a character class to match `A` through `J`.
|
||||
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\/;?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: 64498542cab69128ab24e4de
|
||||
title: Step 36
|
||||
challengeType: 0
|
||||
dashedName: step-36
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
After matching a cell letter successfully, your `rangeRegex` needs to match the cell number. Cell numbers in your sheet range from `1` to `99`.
|
||||
|
||||
Add a capture group after your letter capture group. Your new capture group should match one or two digits – the first digit should be `1` through `9`, and the second digit should be `0` through `9`. The second digit should be optional.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should add a second capture group to your `rangeRegex`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(.*\)\/;?/);
|
||||
```
|
||||
|
||||
Your second capture group should have a character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[.*\]\??\)\/;?/);
|
||||
```
|
||||
|
||||
Your second capture group should have two character classes.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[.*\]\[.*\]\??\)\/;?/);
|
||||
```
|
||||
|
||||
Your first new character class should match the digits `1` through `9`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[.*\]\??\)\/;?/);
|
||||
```
|
||||
|
||||
Your second new character class should match the digits `0` through `9`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\??\)\/;?/);
|
||||
```
|
||||
|
||||
Your second new character class should be optional.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/;?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])/;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,117 @@
|
||||
---
|
||||
id: 6449860d84c9e22cbd7b497c
|
||||
title: Step 37
|
||||
challengeType: 0
|
||||
dashedName: step-37
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Ranges are separated by a colon. After your two capture groups, your `rangeRegex` should look for a colon.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should add a colon after your second capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\/;?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?)/;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: 6449863f592af72d9be0959e
|
||||
title: Step 38
|
||||
challengeType: 0
|
||||
dashedName: step-38
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
After your `rangeRegex` finds the `:`, it needs to look for the same letter and number pattern as it did before.
|
||||
|
||||
Copy your two existing capture groups and paste them after the colon.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should add a third capture group to your `rangeRegex`, after the colon.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(.*\)/);
|
||||
```
|
||||
|
||||
Your third capture group should use a character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[.*\]\)/);
|
||||
```
|
||||
|
||||
Your third capture group should match the characters `A` through `J`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)/);
|
||||
```
|
||||
|
||||
You should add a fourth capture group to your `rangeRegex`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(.*\)/);
|
||||
```
|
||||
|
||||
Your fourth capture group should match one or two digits.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):/;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
id: 6449874d5191562eb3313b3f
|
||||
title: Step 39
|
||||
challengeType: 0
|
||||
dashedName: step-39
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Finally, make your `rangeRegex` global and case-insensitive.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `rangeRegex` should be case-insensitive.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/g?i/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` should be global.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/i?g/);
|
||||
```
|
||||
|
||||
Your `rangeRegex` should be both global and case-insensitive.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/;
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
id: 6449876e7aae0d2f8257a497
|
||||
title: Step 40
|
||||
challengeType: 0
|
||||
dashedName: step-40
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `rangeFromString` arrow function that takes two parameters, `num1` and `num2`. The function should implicitly return the result of calling `range` with `num1` and `num2` as arguments.
|
||||
|
||||
To be safe, parse `num1` and `num2` into integers as you pass them into `range`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `rangeFromString` variable after your `rangeRegex`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*(?:var|let|const)\s+rangeFromString/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `rangeFromString` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString/);
|
||||
```
|
||||
|
||||
Your `rangeFromString` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `rangeFromString` function should have `num1` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(?\s*num1/);
|
||||
```
|
||||
|
||||
Your `rangeFromString` function should have `num2` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your `rangeFromString` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `rangeFromString` function should return the result of calling your `range` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(/);
|
||||
```
|
||||
|
||||
You should call `parseInt` with `num1` as an argument and pass the result to the `range` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)/);
|
||||
```
|
||||
|
||||
You should call `parseInt` with `num2` as the argument and pass the result to the `range` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: 64498b085028fc30a58bb6a7
|
||||
title: Step 41
|
||||
challengeType: 0
|
||||
dashedName: step-41
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a function `elemValue` which takes a `num` parameter. The function should be empty.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `elemValue` variable after your `rangeFromString()` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*(?:var|let|const)\s+elemValue/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `elemValue` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue/);
|
||||
```
|
||||
|
||||
Your `elemValue` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `elemValue` function should have `num` as the only parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `elemValue` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,153 @@
|
||||
---
|
||||
id: 646d0889c6ff4baa46ac1c50
|
||||
title: Step 42
|
||||
challengeType: 0
|
||||
dashedName: step-42
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `elemValue` function, declare a function called `inner` which takes a `character` parameter.
|
||||
|
||||
Then, return your `inner` function.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `inner` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*(?:var|let|const)\s+inner/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `inner` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner/);
|
||||
```
|
||||
|
||||
Your `inner` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `inner` function should have `character` as the only parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `inner` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
You should explicitly return your `inner` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*\};?\s*return\s+inner/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: 646d09a07241aaab1e777080
|
||||
title: Step 43
|
||||
challengeType: 0
|
||||
dashedName: step-43
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `inner` function, return the result of calling `idToText` with `character + num` as the argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `inner` function should use an explicit return.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return/);
|
||||
```
|
||||
|
||||
Your `inner` function should return the result of calling your `idToText` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(/);
|
||||
```
|
||||
|
||||
You should pass `character + num` as the argument to your `idToText` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: 646d0a022da7bcabf3e3aca3
|
||||
title: Step 44
|
||||
challengeType: 0
|
||||
dashedName: step-44
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The concept of returning a function within a function is called <dfn>currying</dfn>. This approach allows you to create a variable that holds a function to be called later, but with a reference to the parameters of the outer function call.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
const innerOne = elemValue(1);
|
||||
const final = innerOne("A");
|
||||
```
|
||||
|
||||
`innerOne` would be your `inner` function, with `num` set to `1`, and `final` would have the value of the cell with the `id` of `A1`. This is possible because functions have access to all variables declared at their creation. This is called <dfn>closure</dfn>.
|
||||
|
||||
You'll get some more practice with this. Declare a function called `addCharacters` which takes a `character1` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `addCharacters` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*(?:var|let|const)\s+addCharacters/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `addCharacters` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters/);
|
||||
```
|
||||
|
||||
Your `addCharacters` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?.*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `addCharacters` function should not use an implicit return.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?.*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `addCharacters` function should have a `character1` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
return idToText(character + num);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
id: 646d0d20108440acc95a6b32
|
||||
title: Step 45
|
||||
challengeType: 0
|
||||
dashedName: step-45
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `elemValue` function, you explicitly declared a function called `inner` and returned it. However, because you are using arrow syntax, you can implicitly return a function. For example:
|
||||
|
||||
```js
|
||||
const curry = soup => veggies => {};
|
||||
```
|
||||
|
||||
`curry` is a function which takes a `soup` parameter and returns a function which takes a `veggies` parameter. Using this syntax, update your `addCharacters` function to return an empty function which takes a `character2` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `addCharacters` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `elemValue` function should return an arrow function which has a `character2` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your inner arrow function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
return idToText(character + num);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
const addCharacters = character1 => {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
id: 646d0db5175974ad8633b71c
|
||||
title: Step 46
|
||||
challengeType: 0
|
||||
dashedName: step-46
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your inner functions can also return a function. Using the same arrow syntax, update your `addCharacters` function to return a third function which takes a `num` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your inner arrow function should return another arrow function with a `num` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your inner-most arrow function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
return idToText(character + num);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
const addCharacters = character1 => character2 => {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
id: 646d0e4636e14eae2bb3b992
|
||||
title: Step 47
|
||||
challengeType: 0
|
||||
dashedName: step-47
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now update your innermost function in the `addCharacters` chain to implicitly return the result of calling `charRange()` with `character1` and `character2` as the arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your innermost function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your innermost function should return the result of calling `charRange()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(/);
|
||||
```
|
||||
|
||||
You should pass `character1` as the first argument to your `charRange()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1/);
|
||||
```
|
||||
|
||||
You should pass `character2` as the second argument to your `charRange()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{\s*const\s+inner\s*=\s*\(?\s*character\s*\)?\s*=>\s*\{\s*return\s+idToText\(\s*character\s*\+\s*num\s*\);?\s*};?\s*return\s+inner;?\s*\}\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
return idToText(character + num);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
const addCharacters = character1 => character2 => num => {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,143 @@
|
||||
---
|
||||
id: 646d1980018efaaec2b1c28b
|
||||
title: Step 48
|
||||
challengeType: 0
|
||||
dashedName: step-48
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Use the same syntax as your `addCharacters` function to update your `elemValue` function. It should no longer declare `inner`, but should return the function implicitly.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `elemValue` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `elemValue` function should implicitly return an arrow function with a `character` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your inner arrow function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your inner arrow function should return the result of calling `idToText()` with `character + num` as the argument.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => {
|
||||
const inner = character => {
|
||||
return idToText(character + num);
|
||||
}
|
||||
return inner;
|
||||
}
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
id: 646d19fc4705e4af65c3e688
|
||||
title: Step 49
|
||||
challengeType: 0
|
||||
dashedName: step-49
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `addCharacters` function ultimately returns a range of characters. You want it to return an array of cell ids. Chain the `.map()` method to your `charRange()` call. Do not pass a callback function yet.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should chain `.map()` to your `charRange()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(/);
|
||||
```
|
||||
|
||||
You should not pass anything to your `.map()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,136 @@
|
||||
---
|
||||
id: 646d1b96dd7ea4b0061458bc
|
||||
title: Step 50
|
||||
challengeType: 0
|
||||
dashedName: step-50
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You can pass a function <dfn>reference</dfn> as a callback parameter. A function reference is a function name without the parentheses. For example:
|
||||
|
||||
```js
|
||||
const myFunc = (val) => `value: ${val}`;
|
||||
const array = [1, 2, 3];
|
||||
const newArray = array.map(myFunc);
|
||||
```
|
||||
|
||||
The `.map()` method here will call the `myFunc` function, passing the same arguments that a `.map()` callback takes. The first argument is the value of the array at the current iteration, so `newArray` would be `[value: 1, value: 2, value: 3]`.
|
||||
|
||||
Pass a reference to your `elemValue` function as the callback to your `.map()` method.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should not call your `elemValue` function.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\)\s*\)/);
|
||||
```
|
||||
|
||||
You should pass a reference to `elemValue` as the callback to your `.map()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map();
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,128 @@
|
||||
---
|
||||
id: 646d1cadf0d96ab0b7e12da4
|
||||
title: Step 51
|
||||
challengeType: 0
|
||||
dashedName: step-51
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Because `elemValue` returns a function, your `addChars` function ultimately returns an array of function references. You want the `.map()` method to run the inner function of your `elemValue` function, which means you need to call `elemValue` instead of reference it. Pass `num` as the argument to your `elemValue` function.
|
||||
|
||||
<!-- TODO: Explain further? -->
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call `elemValue()` in your `.map()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(/);
|
||||
```
|
||||
|
||||
You should pass `num` to your `elemValue()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
id: 646d1d67f9261fb15a795588
|
||||
title: Step 52
|
||||
challengeType: 0
|
||||
dashedName: step-52
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `rangeExpanded` variable and assign it the result of calling the `.replace()` method of your `x` parameter. Pass the `rangeRegex` variable as the argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `rangeExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*(?:let|var|const)\s+rangeExpanded/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `rangeExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded/);
|
||||
```
|
||||
|
||||
You should assign the result of calling `.replace()` on `x` to your `rangeExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(/);
|
||||
```
|
||||
|
||||
You should pass `rangeRegex` as the argument to `.replace()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
id: 646d1e531042dfb24da1f032
|
||||
title: Step 53
|
||||
challengeType: 0
|
||||
dashedName: step-53
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The second argument to the `.replace()` method does not have to be a string. You can instead pass a callback function to run more complex logic on the matched string.
|
||||
|
||||
The callback function takes a few parameters. The first is the matched string. Pass an empty callback function to your `.replace()` call, and give it a `match` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should pass an arrow function as the second argument to your `.replace()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(.*\)\s*=>\s*\{\s*\}\)/);
|
||||
```
|
||||
|
||||
Your arrow function should take a `match` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*\)\s*=>\s*\{\s*\}\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex);
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
id: 646d3141790b3cb337dd611a
|
||||
title: Step 54
|
||||
challengeType: 0
|
||||
dashedName: step-54
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The callback function then has a parameter for each capture group in the regular expression. In your case, `rangeRegex` has four capture groups: the first letter, the first numbers, the second letter, and the second numbers.
|
||||
|
||||
Give your callback function four more parameters to match those capture groups: `char1`, `num1`, `char2`, and `num2`. `char` will be short for `character`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should pass `char1` as the second argument to your callback.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1/);
|
||||
```
|
||||
|
||||
You should pass `num1` as the third argument to your callback.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1/);
|
||||
```
|
||||
|
||||
You should pass `char2` as the fourth argument to your callback.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2/);
|
||||
```
|
||||
|
||||
You should pass `num2` as the fifth argument to your callback.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match) => {});
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
id: 646d382c4d70ceb3dba1e830
|
||||
title: Step 55
|
||||
challengeType: 0
|
||||
dashedName: step-55
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Have your callback implicitly return the result of calling `rangeFromString()` with `num1` and `num2` as the arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback should return the result of calling `rangeFromString()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(/);
|
||||
```
|
||||
|
||||
You should pass `num1` as the first argument to your `rangeFromString()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1/);
|
||||
```
|
||||
|
||||
You should pass `num2` as the second argument to your `rangeFromString()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => {});
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 646d386a685620b49db4be76
|
||||
title: Step 56
|
||||
challengeType: 0
|
||||
dashedName: step-56
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Call the `.map()` method on your `rangeFromString()` call, passing a reference to `addCharacters` as the callback function.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call the `.map()` method on your `rangeFromString()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(/);
|
||||
```
|
||||
|
||||
You should pass a reference to `addCharacters` as the callback to your `.map()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2));
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,127 @@
|
||||
---
|
||||
id: 646d38c326f3c8b54023de38
|
||||
title: Step 57
|
||||
challengeType: 0
|
||||
dashedName: step-57
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`addCharacters` returns a function, so you'll want to call it. Pass `char1` as the argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should call your `addCharacters()` function in your `.map()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*/);
|
||||
```
|
||||
|
||||
You should pass `char1` as the argument to your `addCharacters()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters));
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,133 @@
|
||||
---
|
||||
id: 646d38f906b94cb5fe6ce7de
|
||||
title: Step 58
|
||||
challengeType: 0
|
||||
dashedName: step-58
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `addCharacters(char1)` is also returning a function, which returns another function. You need to make another function call to access that innermost function reference for the `.map()` callback. JavaScript allows you to chain function calls:
|
||||
|
||||
```js
|
||||
myFunc(1)("hi");
|
||||
```
|
||||
|
||||
Chain a function call to your `addCharacters(char1)` call, and pass `char2` as the argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should chain a function call to your `addCharacters(char1)` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(/);
|
||||
```
|
||||
|
||||
You should pass `char2` as the argument to your chained function call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)));
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,125 @@
|
||||
---
|
||||
id: 646d3952f6af37b6a1c241c2
|
||||
title: Step 59
|
||||
challengeType: 0
|
||||
dashedName: step-59
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that your `.map()` function is receiving the innermost function reference from `addCharacters`, it will properly iterate over the elements and pass each element as `n` to that function.
|
||||
|
||||
You'll notice that you are not using your `match` parameter. In JavaScript, it is common convention to prefix an unused parameter with an underscore `_`. You could also leave the parameter empty like so: `(, char1)` but it is often clearer to name the parameter for future readability.
|
||||
|
||||
Prefix your `match` parameter with an underscore.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should prefix your `match` parameter with an underscore.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 646d39c156fe94b7482c3ab6
|
||||
title: Step 60
|
||||
challengeType: 0
|
||||
dashedName: step-60
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a variable `cellRegex` to match cell references. It should match a letter from `A` to `J`, followed by a digit from `1` to `9`, and an optional digit from `0` to `9`. Make the regular expression case-insensitive and global.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `cellRegex` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*(?:var|let|const)\s+cellRegex/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `cellRegex` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex/);
|
||||
```
|
||||
|
||||
You should assign a regular expression to your `cellRegex` variables.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\//);
|
||||
```
|
||||
|
||||
Your regular expression should use a character class to match the characters from `A` to `J`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]/);
|
||||
```
|
||||
|
||||
Your regular expression should use a character class to match the digits from `1` to `9`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]/);
|
||||
```
|
||||
|
||||
Your regular expression should use a character class to match the digits from `0` to `9`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]/);
|
||||
```
|
||||
|
||||
Your third character class should be optional.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?/);
|
||||
```
|
||||
|
||||
Your regular expression should be case-insensitive and global.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 646d3b27cd3c56b875256301
|
||||
title: Step 61
|
||||
challengeType: 0
|
||||
dashedName: step-61
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `cellExpanded` variable and assign it the value of calling `.replace()` on your `rangeExpanded` variable. Pass it your `cellRegex` and an empty callback function. The callback function should take a `match` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `cellExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*(var|let|const)\s+cellExpanded/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `cellExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded/);
|
||||
```
|
||||
|
||||
You should assign `cellExpanded` the result of calling the `.replace()` method of `rangeExpanded`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(/);
|
||||
```
|
||||
|
||||
You should pass `cellRegex` as the first argument to your `.replace()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex/);
|
||||
```
|
||||
|
||||
You should pass a callback function using arrow syntax as the second argument to your `.replace()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*(?:match)?\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should have a `match` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,141 @@
|
||||
---
|
||||
id: 646d3bc75fe0c9b972da3323
|
||||
title: Step 62
|
||||
challengeType: 0
|
||||
dashedName: step-62
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Update your callback function to return the result of calling `idToText()` with `match` as the argument. Remember that your regular expression is case-insensitive, so you will need to call `toUpperCase()` on `match` before passing it to `idToText()`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should call `idToText()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(/);
|
||||
```
|
||||
|
||||
You should pass `match` to your `idToText()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\s*/);
|
||||
```
|
||||
|
||||
You should call the `.toUpperCase()` method of `match` as you pass it to `idToText()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => {})
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
id: 646d3c146e10b0ba222bb2a7
|
||||
title: Step 63
|
||||
challengeType: 0
|
||||
dashedName: step-63
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In mathematics, an <dfn>infix</dfn> is a mathematical operator that appears between its two operands. For example, `1 + 2` is an infix expression.
|
||||
|
||||
To parse these expressions, you will need to map the symbols to relevant functions. Declare an `infixToFunction` variable, and assign it an empty object.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `infixToFunction` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:const|let|var)\s+infixToFunction/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `infixToFunction` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixToFunction/);
|
||||
```
|
||||
|
||||
Your `infixToFunction` variable should be an object.
|
||||
|
||||
```js
|
||||
assert.isObject(infixToFunction);
|
||||
```
|
||||
|
||||
Your `infixToFunction` object should be empty.
|
||||
|
||||
```js
|
||||
assert.lengthOf(Object.keys(infixToFunction), 0);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
id: 646d3d037872fbbae0a8ec0e
|
||||
title: Step 64
|
||||
challengeType: 0
|
||||
dashedName: step-64
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Object values do not have to be primitive types, like a string or a number. They can also be functions.
|
||||
|
||||
Give your `infixToFunction` object a `+` property. That property should be a function that takes an `x` and `y` parameter and implicitly returns the sum of those two parameters.
|
||||
|
||||
Because `+` is not alphanumeric, you'll need to wrap it in quotes for your property.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `infixToFunction` object should have a `+` property.
|
||||
|
||||
```js
|
||||
assert.property(infixToFunction, '+');
|
||||
```
|
||||
|
||||
Your `+` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(infixToFunction['+']);
|
||||
```
|
||||
|
||||
Your `+` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixToFunction\s*=\s*\{\s*('|"|`)\+\1\s*:\s*\(/);
|
||||
```
|
||||
|
||||
Your `+` function should have `x` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixToFunction\s*=\s*\{\s*('|"|`)\+\1\s*:\s*\(\s*x/);
|
||||
```
|
||||
|
||||
Your `+` function should have `y` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixToFunction\s*=\s*\{\s*('|"|`)\+\1\s*:\s*\(\s*x\s*,\s*y\s*\)/);
|
||||
```
|
||||
|
||||
Your `+` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+infixToFunction\s*=\s*\{\s*('|"|`)\+\1\s*:\s*\(\s*x\s*,\s*y\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
Your `+` function should return the sum of `x` and `y`.
|
||||
|
||||
```js
|
||||
assert.equal(infixToFunction['+'](1, 2), 3);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const infixToFunction = {
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 646d3d65be79c8bb9c7df9ff
|
||||
title: Step 65
|
||||
challengeType: 0
|
||||
dashedName: step-65
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now create a `-` property that is a function that takes an `x` and `y` parameter and implicitly returns the result of subtracting `y` from `x`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `infixToFunction` object should have a `-` property.
|
||||
|
||||
```js
|
||||
assert.property(infixToFunction, '-');
|
||||
```
|
||||
|
||||
Your `-` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(infixToFunction['-']);
|
||||
```
|
||||
|
||||
Your `-` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)-\1\s*:\s*\(/);
|
||||
```
|
||||
|
||||
Your `-` function should have `x` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)-\1\s*:\s*\(\s*x/);
|
||||
```
|
||||
|
||||
Your `-` function should have `y` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)-\1\s*:\s*\(\s*x\s*,\s*y\s*\)/);
|
||||
```
|
||||
|
||||
Your `-` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /('|"|`)-\1\s*:\s*\(\s*x\s*,\s*y\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
Your `-` function should return the result of subtracting `y` from `x`.
|
||||
|
||||
```js
|
||||
assert.equal(infixToFunction['-'](1, 2), -1);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,207 @@
|
||||
---
|
||||
id: 646d3d80c3b4aebc4103618e
|
||||
title: Step 66
|
||||
challengeType: 0
|
||||
dashedName: step-66
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Following the same pattern, add a property for multiplication `*` and division `/` with the appropriate functions.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `infixToFunction` object should have a `*` property.
|
||||
|
||||
```js
|
||||
assert.property(infixToFunction, '*');
|
||||
```
|
||||
|
||||
Your `*` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(infixToFunction['*']);
|
||||
```
|
||||
|
||||
Your `*` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\*\1\s*:\s*\(/);
|
||||
```
|
||||
|
||||
Your `*` function should have `x` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\*\1\s*:\s*\(\s*x/);
|
||||
```
|
||||
|
||||
Your `*` function should have `y` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\*\1\s*:\s*\(\s*x\s*,\s*y\s*\)/);
|
||||
```
|
||||
|
||||
Your `*` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /('|"|`)\*\1\s*:\s*\(\s*x\s*,\s*y\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
Your `*` function should return the result of multiplying `x` by `y`.
|
||||
|
||||
```js
|
||||
assert.equal(infixToFunction['*'](2, 5), 10);
|
||||
```
|
||||
|
||||
Your `infixToFunction` object should have a `/` property.
|
||||
|
||||
```js
|
||||
assert.property(infixToFunction, '/');
|
||||
```
|
||||
|
||||
Your `/` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(infixToFunction['/']);
|
||||
```
|
||||
|
||||
Your `/` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\/\1\s*:\s*\(/);
|
||||
```
|
||||
|
||||
Your `/` function should have `x` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\/\1\s*:\s*\(\s*x/);
|
||||
```
|
||||
|
||||
Your `/` function should have `y` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /('|"|`)\/\1\s*:\s*\(\s*x\s*,\s*y\s*\)/);
|
||||
```
|
||||
|
||||
Your `/` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /('|"|`)\/\1\s*:\s*\(\s*x\s*,\s*y\s*\)\s*\{/);
|
||||
```
|
||||
|
||||
Your `/` function should return the result of dividing `x` by `y`.
|
||||
|
||||
```js
|
||||
assert.equal(infixToFunction['/'](10, 2), 5);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
--fcc-editable-region--
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
id: 646d3da8501e15bcd355ba1d
|
||||
title: Step 67
|
||||
challengeType: 0
|
||||
dashedName: step-67
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you have your infix functions, you need a way to evaluate them. Declare an `infixEval` function which takes two parameters, `str` and `regex`. It should implicitly return the `.replace()` method of `str`, with `regex` and an empty callback as the arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `infixEval` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:var|let|const)\s+infixEval\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `infixEval` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=/);
|
||||
```
|
||||
|
||||
Your `infixEval` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(infixEval);
|
||||
```
|
||||
|
||||
Your `infixEval` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(/);
|
||||
```
|
||||
|
||||
Your `infixEval` function should have `str` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str/);
|
||||
```
|
||||
|
||||
Your `infixEval` function should have `regex` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)/);
|
||||
```
|
||||
|
||||
Your `infixEval` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `infixEval` function should return the result of calling the `.replace()` method on `str`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(/);
|
||||
```
|
||||
|
||||
You should pass `regex` as the first argument to the `.replace()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex/);
|
||||
```
|
||||
|
||||
You should pass an empty arrow function as the second argument to the `.replace()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*\)\s*=>\s*\{\s*\}\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 646d3e135ab3abbdbfe5c899
|
||||
title: Step 68
|
||||
challengeType: 0
|
||||
dashedName: step-68
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your callback needs four parameters. `match`, `arg1`, `operator`, and `arg2`.
|
||||
|
||||
You will not be using the `match` parameter, so remember to prefix it.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback function should have `match` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_?match/);
|
||||
```
|
||||
|
||||
Your `match` parameter should be prefixed with an underscore.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match/);
|
||||
```
|
||||
|
||||
Your callback function should have `arg1` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1/);
|
||||
```
|
||||
|
||||
Your callback function should have `operator` as the third parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator/);
|
||||
```
|
||||
|
||||
Your callback function should have `arg2` as the fourth parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)/);
|
||||
```
|
||||
|
||||
Your callback function should still be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*\{\s*\}\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const infixEval = (str, regex) => str.replace(regex, () => {});
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
id: 646d3e64b15f92be6e61704e
|
||||
title: Step 69
|
||||
challengeType: 0
|
||||
dashedName: step-69
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `regex` you will be passing to your `infixEval` function will match two numbers with an operator between them. The first number will be assigned to `arg1` in the callback, the second to `arg2`, and the operator to `operator`.
|
||||
|
||||
Have your callback function implicitly return the `operator` property of your `infixToFunction` object. Remember that `operator` is a variable which holds the property name, not the actual property name.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should access the `infixToFunction` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction/);
|
||||
```
|
||||
|
||||
Your callback function should use bracket notation to access the property of the `infixToFunction` object that matches the value of the `operator` argument.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => {});
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
id: 646d3ee7b17ae3bf48610033
|
||||
title: Step 70
|
||||
challengeType: 0
|
||||
dashedName: step-70
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
`infixToFunction[operator]` returns a function. Call that function directly, passing `arg1` and `arg2` as the arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback function should return the result of calling `infixToFunction[operator]`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\(/);
|
||||
```
|
||||
|
||||
You should pass `arg1` as the first argument to your `infixToFunction[operator]` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\(\s*arg1/);
|
||||
```
|
||||
|
||||
You should pass `arg2` as the second argument to your `infixToFunction[operator]` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\(\s*arg1\s*,\s*arg2\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator]);
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
id: 646d3f1fd12f76c02c823bb8
|
||||
title: Step 71
|
||||
challengeType: 0
|
||||
dashedName: step-71
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You have a slight bug. `arg1` and `arg2` are strings, not numbers. `infixToFunction['+']("1", "2")` would return `12`, which is not mathematically correct.
|
||||
|
||||
Wrap each of your `infixToFunction[operator]` arguments in a `parseFloat()` call.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should wrap `arg1` in a `parseFloat()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\(\s*parseFloat\(\s*arg1\s*\)\s*,/);
|
||||
```
|
||||
|
||||
You should wrap `arg2` in a `parseFloat()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+infixEval\s*=\s*\(\s*str\s*,\s*regex\s*\)\s*=>\s*str\.replace\(\s*regex\s*,\s*\(\s*_match\s*,\s*arg1\s*,\s*operator\s*,\s*arg2\s*\)\s*=>\s*infixToFunction\[\s*operator\s*\]\(\s*parseFloat\(\s*arg1\s*\)\s*,\s*parseFloat\(\s*arg2\s*\)\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](arg1, arg2));
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 646d3f718b5f8dc102cd528e
|
||||
title: Step 72
|
||||
challengeType: 0
|
||||
dashedName: step-72
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you can evaluate mathematical expressions, you need to account for order of operations. Declare a `highPrecedence` function that takes a `str` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `highPrecedence` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:var|let|const)\s+highPrecedence/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `highPrecedence` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence/);
|
||||
```
|
||||
|
||||
Your `highPrecedence` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(highPrecedence);
|
||||
```
|
||||
|
||||
Your `highPrecedence` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*(?:str)?\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `highPrecedence` function should have a `str` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `highPrecedence` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,198 @@
|
||||
---
|
||||
id: 646d404259f512c1a9e86ac1
|
||||
title: Step 73
|
||||
challengeType: 0
|
||||
dashedName: step-73
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
In your `highPrecedence` function, declare a `regex` variable. Assign it a regular expression that matches a number (including decimal numbers) followed by a `*` or `/` operator followed by another number.
|
||||
|
||||
Each number, and the operator, should be in separate capture groups.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `regex` variable in your `highPrecedence` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*(?:const|let|var)\s+regex/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `regex` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex/);
|
||||
```
|
||||
|
||||
Your `regex` variable should be a regular expression.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\//);
|
||||
```
|
||||
|
||||
Your `regex` should use a capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(/);
|
||||
```
|
||||
|
||||
Your first capture group should use a character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[/);
|
||||
```
|
||||
|
||||
Your first capture group should match any digit or a period. Use the special `\d` character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]/);
|
||||
```
|
||||
|
||||
Your first capture group should match the character class one or more times.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a second capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(/);
|
||||
```
|
||||
|
||||
Your second capture group should match a `*` or `/` operator. Use a character class in the capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)/);
|
||||
```
|
||||
|
||||
Your `regex` should use a third capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(/);
|
||||
```
|
||||
|
||||
Your third capture group should be the same as your first capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,163 @@
|
||||
---
|
||||
id: 646d40c543943ec250039682
|
||||
title: Step 74
|
||||
challengeType: 0
|
||||
dashedName: step-74
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you have a regular expression to match multiplication or division, you can evaluate that expression.
|
||||
|
||||
Declare a `str2` variable and assign it the result of calling `infixEval` with `str` and `regex` as arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `str2` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*(?:const|let|var)\s+str2/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `str2` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2/);
|
||||
```
|
||||
|
||||
You should assign `str2` the result of calling your `infixEval` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(/);
|
||||
```
|
||||
|
||||
You should pass `str` as the first argument to your `infixEval` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str/);
|
||||
```
|
||||
|
||||
You should pass `regex` as the second argument to your `infixEval` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
id: 646d40fe4b7b50c30c2b4cd8
|
||||
title: Step 75
|
||||
challengeType: 0
|
||||
dashedName: step-75
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `infixEval` function will only evaluate the first multiplication or division operation, because `regex` isn't global. This means you'll want to use a recursive approach to evaluate the entire string.
|
||||
|
||||
If `infixEval` does not find any matches, it will return the `str` value as-is. Using a ternary expression, check if `str2` is equal to `str`. If it is, return `str`, otherwise return the result of calling `highPrecedence()` on `str2`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `infixEval` function should use the `return` keyword.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return/);
|
||||
```
|
||||
|
||||
You should use the `return` keyword with a condition to check if `str` is equal to `str2`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return\s+(?:str\s*===\s*str2|str2\s*===\s*str)/);
|
||||
```
|
||||
|
||||
You should use ternary syntax with your `return` statement.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return\s+(?:str\s*===\s*str2|str2\s*===\s*str)\s*\?/);
|
||||
```
|
||||
|
||||
If the ternary condition is true, you should return `str`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return\s+(?:str\s*===\s*str2|str2\s*===\s*str)\s*\?\s*str/);
|
||||
```
|
||||
|
||||
If the ternary condition is false, you should return the result of calling `highPrecedence()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return\s+(?:str\s*===\s*str2|str2\s*===\s*str)\s*\?\s*str\s*:\s*highPrecedence\(/);
|
||||
```
|
||||
|
||||
You should pass `str2` to your `highPrecedence()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+highPrecedence\s*=\s*\(?\s*str\s*\)?\s*=>\s*{\s*const\s+regex\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\*\\\/|\\\/*)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*str\s*,\s*regex\s*\);?\s*return\s+(?:str\s*===\s*str2|str2\s*===\s*str)\s*\?\s*str\s*:\s*highPrecedence\(\s*str2\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
--fcc-editable-region--
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,170 @@
|
||||
---
|
||||
id: 646d41e23b583fc3b8cc4579
|
||||
title: Step 76
|
||||
challengeType: 0
|
||||
dashedName: step-76
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you can start applying your function parsing logic to a string. Declare a function called `applyFunction`, which takes a `str` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `applyFunction` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /(?:var|let|const)\s+applyFunction\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `applyFunction` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=/);
|
||||
```
|
||||
|
||||
Your `applyFunction` variable should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(applyFunction);
|
||||
```
|
||||
|
||||
Your `applyFunction` function should use arrow syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*(?:str)?\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `applyFunction` function should have a `str` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `applyFunction` should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,160 @@
|
||||
---
|
||||
id: 646d423fade4a9c4636acd13
|
||||
title: Step 77
|
||||
challengeType: 0
|
||||
dashedName: step-77
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
First you need to handle the higher precedence operators. Declare a `noHigh` variable, and assign it the result of calling `highPrecedence()` with `str` as an argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `noHigh` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*(?:var|let|const)\s+noHigh\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `noHigh` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=/);
|
||||
```
|
||||
|
||||
You should assign `noHigh` the result of calling `highPrecedence()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(/);
|
||||
```
|
||||
|
||||
You should pass `str` as the argument to your `highPrecedence()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,205 @@
|
||||
---
|
||||
id: 646d42f58deb2fc52adc6611
|
||||
title: Step 78
|
||||
challengeType: 0
|
||||
dashedName: step-78
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now that you've parsed and evaluated the multiplication and division operators, you need to do the same with the addition and subtraction operators.
|
||||
|
||||
Declare an `infix` variable, and assign it a regular expression that matches a number (including decimal numbers) followed by a `+` or `-` operator followed by another number.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `infix` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*(?:const|let|var)\s+infix\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `infix` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=/);
|
||||
```
|
||||
|
||||
Your `infix` variable should be a regular expression.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\//);
|
||||
```
|
||||
|
||||
Your `infix` regex should use a capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(/);
|
||||
```
|
||||
|
||||
Your first capture group should use a character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[/);
|
||||
```
|
||||
|
||||
Your first capture group should match one or more digits or decimal points. Use the `\d` character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)/);
|
||||
```
|
||||
|
||||
Your `infix` regex should use a second capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(/);
|
||||
```
|
||||
|
||||
Your second capture group should use a character class.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[/);
|
||||
```
|
||||
|
||||
Your second capture group should match either the `+` or `-` operator.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)/);
|
||||
```
|
||||
|
||||
Your `infix` regex should use a third capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(/);
|
||||
```
|
||||
|
||||
Your third capture group should be the same as your first capture group.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
id: 646d43587d926bc5b6cb2e50
|
||||
title: Step 79
|
||||
challengeType: 0
|
||||
dashedName: step-79
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `str2` variable, and assign it the result of calling `infixEval()` with `noHigh` and `infix` as arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `str2` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*(?:let|var|const)\s+str2/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `str2` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2/);
|
||||
```
|
||||
|
||||
You should assign `str2` the result of calling `infixEval()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(/);
|
||||
```
|
||||
|
||||
You should pass `noHigh` as the first argument to `infixEval()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh/);
|
||||
```
|
||||
|
||||
You should pass `infix` as the second argument to `infixEval()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 646d448479c8fdc8dcec868c
|
||||
title: Step 80
|
||||
challengeType: 0
|
||||
dashedName: step-80
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `functionCall` variable, and assign it this regular expression: `/([a-z]*)\(([0-9., ]*)\)(?!.*\()/i`
|
||||
|
||||
This expression will look for function calls like `sum(1, 4)`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `functionCall` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*(?:const|let|var)\s+functionCall\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `functionCall` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=/);
|
||||
```
|
||||
|
||||
You should assign `functionCall` the provided regular expression.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,195 @@
|
||||
---
|
||||
id: 646d44da986f2bc9b72f5fe2
|
||||
title: Step 81
|
||||
challengeType: 0
|
||||
dashedName: step-81
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare a `toNumberList` function which takes an `args` parameter, and returns the result of splitting the `args` by commas, and mapping the resulting array to `parseFloat`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `toNumberList` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*(?:const|let|var)\s+toNumberList\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `toNumberList` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=/);
|
||||
```
|
||||
|
||||
Your `toNumberList` variable should be an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*(?:args)?\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `toNumberList` function should have an `args` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>/);
|
||||
```
|
||||
|
||||
Your `toNumberList` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `toNumberList` function should return the result of calling the `.split()` method of `args`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(/);
|
||||
```
|
||||
|
||||
You should split `args` on the `,` character.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)/);
|
||||
```
|
||||
|
||||
You should chain the `.map()` method to the `.split()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(/);
|
||||
```
|
||||
|
||||
You should pass a reference to `parseFloat` as the callback to `.map()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?/);
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,177 @@
|
||||
---
|
||||
id: 646d451c2e44afca71b67818
|
||||
title: Step 82
|
||||
challengeType: 0
|
||||
dashedName: step-82
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Declare an `apply` function that takes a `fn` and `args` parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare an `apply` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*(?:var|let|const)\s+apply\s*=/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `apply` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=/);
|
||||
```
|
||||
|
||||
Your `apply` variable should be assigned an arrow function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(/);
|
||||
```
|
||||
|
||||
Your `apply` function should have `fn` as its first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn/);
|
||||
```
|
||||
|
||||
Your `apply` function should have `args` as its second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)/);
|
||||
```
|
||||
|
||||
Your `apply` function should be empty.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,169 @@
|
||||
---
|
||||
id: 646d4554721d43cb19a68bc4
|
||||
title: Step 83
|
||||
challengeType: 0
|
||||
dashedName: step-83
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The `fn` parameter will be the name of a function, such as `SUM`. Update `apply` to implicitly return the function found at the `fn` property of your `spreadsheetFunctions` object.
|
||||
|
||||
Remember that `fn` might not be lowercase, so you'll need to convert it to a lowercase string.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `apply` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your `apply` function should access the `spreadsheetFunctions` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions/);
|
||||
```
|
||||
|
||||
Your `apply` function should access the property of the `spreadsheetFunctions` object that matches the `fn` value.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn/);
|
||||
```
|
||||
|
||||
Your `apply` function should call the `.toLowerCase()` method on `fn` in the property access.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]/);
|
||||
```
|
||||
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => {}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,159 @@
|
||||
---
|
||||
id: 646d45b739da5ecbf830c108
|
||||
title: Step 84
|
||||
challengeType: 0
|
||||
dashedName: step-84
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your `apply` function is returning the spreadsheet function, but not actually applying it. Update `apply` to call the function. Pass in the result of calling `toNumberList` with `args` as an argument.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `apply` function should call the `spreadsheetFunctions[fn.toLowerCase()]` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(/);
|
||||
```
|
||||
|
||||
You should pass a `toNumberList()` call to your `spreadsheetFunctions[fn.toLowerCase()]` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(/);
|
||||
```
|
||||
|
||||
You should pass `args` to your `toNumberList()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()];
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,161 @@
|
||||
---
|
||||
id: 646d45ee725632cca2555146
|
||||
title: Step 85
|
||||
challengeType: 0
|
||||
dashedName: step-85
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now your `applyFunction` needs to return a result. Return the result of calling the `.replace()` method on `str2`. Pass your `functionCall` regex and an empty callback.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `applyFunction` function should return the result of calling the `.replace()` method on `str2`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(/);
|
||||
```
|
||||
|
||||
You should pass `functionCall` as the first argument to your `.replace()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall/);
|
||||
```
|
||||
|
||||
You should pass an empty arrow function as the second argument to your `.replace()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*\)\s*=>\s*\{\s*\}/);
|
||||
```
|
||||
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,186 @@
|
||||
---
|
||||
id: 646d4626420eeecd51f241c2
|
||||
title: Step 86
|
||||
challengeType: 0
|
||||
dashedName: step-86
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Update the callback function to take `match`, `fn`, and `args` as parameters. It should implicitly return the result of checking whether `spreadsheetFunctions` has its own property of `fn`.
|
||||
|
||||
Remember to make `fn` lower case.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback function should have `match` as the first parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match/);
|
||||
```
|
||||
|
||||
Your callback function should have `fn` as the second parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn/);
|
||||
```
|
||||
|
||||
Your callback function should have `args` as the third parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>/);
|
||||
```
|
||||
|
||||
Your callback function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*\{/);
|
||||
```
|
||||
|
||||
Your callback function should return the result of calling the `.hasOwnProperty()` method on the `spreadsheetFunctions` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(/);
|
||||
```
|
||||
|
||||
You should pass `fn` to the .`hasOwnProperty()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn/);
|
||||
```
|
||||
|
||||
You should call the `.toLowerCase()` method on `fn`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, () => {})
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,172 @@
|
||||
---
|
||||
id: 646d467c6994f4ce0dc416a4
|
||||
title: Step 87
|
||||
challengeType: 0
|
||||
dashedName: step-87
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Use the ternary operator to turn your `.hasOwnProperty()` call into the condition. If the object has the property, return the result of calling `apply` with `fn` and `args` as arguments. Otherwise, return `match`.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your callback function should use ternary syntax.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)\s*\?/);
|
||||
```
|
||||
|
||||
If the ternary condition is true, your callback function should return the result of calling `apply()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)\s*\?\s*apply\(/);
|
||||
```
|
||||
|
||||
You should pass `fn` as the first argument to your `apply()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)\s*\?\s*apply\(\s*fn/);
|
||||
```
|
||||
|
||||
You should pass `args` as the second argument to your `apply()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)\s*\?\s*apply\(\s*fn\s*,\s*args\s*\)/);
|
||||
```
|
||||
|
||||
If the ternary is false, you should return `match`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+applyFunction\s*=\s*\(?\s*str\s*\)?\s*=>\s*\{\s*const\s+noHigh\s*=\s*highPrecedence\(\s*str\s*\);?\s*const\s+infix\s*=\s*\/\(\[(?:\\d\.|\.\\d)\]\+\)\(\[(?:\+-|-\+)\]\)\(\[(?:\\d\.|\.\\d)\]\+\)\/;?\s*const\s+str2\s*=\s*infixEval\(\s*noHigh\s*\,\s*infix\s*\);?\s*const\s+functionCall\s*=\s*\/\(\[a-z\]\*\)\\\(\(\[0-9\., \]\*\)\\\)\(\?!\.\*\\\(\)\/i;?\s*const\s+toNumberList\s*=\s*\(?\s*args\s*\)?\s*=>\s*args\.split\(\s*('|"|`),\1\s*\)\.map\(\s*parseFloat\s*\);?\s*const\s+apply\s*=\s*\(\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\[fn\.toLowerCase\(\)\]\(\s*toNumberList\(\s*args\s*\)\);?\s*return\s+str2\.replace\(\s*functionCall\s*,\s*\(\s*match\s*,\s*fn\s*,\s*args\s*\)\s*=>\s*spreadsheetFunctions\.hasOwnProperty\(fn\.toLowerCase\(\s*\)\s*\)\s*\?\s*apply\(\s*fn\s*,\s*args\s*\)\s*:\s*match/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) );
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,167 @@
|
||||
---
|
||||
id: 646d46c03e7d02cecb30f021
|
||||
title: Step 88
|
||||
challengeType: 0
|
||||
dashedName: step-88
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now you can start applying your function parser to your `evalFormula` logic. Declare a `functionExpanded` variable, and assign it the result of calling `applyFunction` with your `cellExpanded` string.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should declare a `functionExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*/);
|
||||
```
|
||||
|
||||
You should use `const` to declare your `functionExpanded` variable.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*const\s+functionExpanded\s*=\s*/);
|
||||
```
|
||||
|
||||
You should assign the `functionExpanded` variable the result of calling your `applyFunction` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(/);
|
||||
```
|
||||
|
||||
You should pass `cellExpanded` to your `applyFunction` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,188 @@
|
||||
---
|
||||
id: 646d4717a689e1cfa232e357
|
||||
title: Step 89
|
||||
challengeType: 0
|
||||
dashedName: step-89
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Like you did with your `highPrecedence()` function, your `evalFormula()` function needs to ensure it has evaluated and replaced everything.
|
||||
|
||||
Use a ternary to check if `functionExpanded` is equal to the original string `x`. If it is, return `functionExpanded`, otherwise return the result of calling `evalFormula()` again with `functionExpanded` and `cells` as arguments.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `evalFormula` function should use the `return` keyword.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return/);
|
||||
```
|
||||
|
||||
Your `return` statement should check if `functionExpanded` is equal to `x`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)/);
|
||||
```
|
||||
|
||||
Your `return` statement should use a ternary operator.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)\s*\?/);
|
||||
```
|
||||
|
||||
If the ternary condition is true, your `evalFormula()` should return `functionExpanded`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)\s*\?\s*functionExpanded/);
|
||||
```
|
||||
|
||||
If the ternary condition is false, your `evalFormula()` should return the result of calling `evalFormula()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)\s*\?\s*functionExpanded\s*:\s*evalFormula\(/);
|
||||
```
|
||||
|
||||
You should pass `functionExpanded` as the first argument to your `evalFormula()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)\s*\?\s*functionExpanded\s*:\s*evalFormula\(\s*functionExpanded/);
|
||||
```
|
||||
|
||||
You should pass `cells` as the second argument to your `evalFormula()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s*evalFormula\s*=\s*\(\s*x\s*,\s*cells\s*\)\s*=>\s*{\s*const\s+idToText\s*=\s*\(?\s*id\s*\)?\s*=>\s*cells\.find\(\s*\(?\s*cell\s*\)?\s*=>\s*(?:cell\.id\s*===\s*id|id\s*===\s*cell\.id)\s*\)\.value;?\s*const\s+rangeRegex\s*=\s*\/\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\):\(\[A-J\]\)\(\[1-9\]\[0-9\]\?\)\/(gi|ig);?\s*const\s+rangeFromString\s*=\s*\(\s*num1\s*,\s*num2\s*\)\s*=>\s*range\(\s*parseInt\(\s*num1\s*\)\s*,\s*parseInt\(\s*num2\s*\)\s*\);?\s*const\s+elemValue\s*=\s*\(?\s*num\s*\)?\s*=>\s*\(?\s*character\s*\)?\s*=>\s*idToText\(\s*character\s*\+\s*num\s*\);?\s*const\s+addCharacters\s*=\s*\(?\s*character1\s*\)?\s*=>\s*\(?\s*character2\s*\)?\s*=>\s*\(?\s*num\s*\)?\s*=>\s*charRange\(\s*character1\s*,\s*character2\s*\)\.map\(\s*elemValue\(\s*num\s*\)\s*\);?\s*const\s+rangeExpanded\s*=\s*x\.replace\(\s*rangeRegex\s*,\s*\(\s*_match\s*,\s*char1\s*,\s*num1\s*,\s*char2\s*,\s*num2\s*\)\s*=>\s*rangeFromString\(\s*num1\s*,\s*num2\s*\)\.map\(\s*addCharacters\s*\(\s*char1\s*\)\(\s*char2\s*\)\s*\)\s*\);?\s*const\s+cellRegex\s*=\s*\/\[A-J\]\[1-9\]\[0-9\]\?\/(gi|ig);?\s*const\s+cellExpanded\s*=\s*rangeExpanded\.replace\(\s*cellRegex\s*,\s*\(?\s*match\s*\)?\s*=>\s*idToText\(\s*match\.toUpperCase\(\)\s*\)\);?\s*(?:const|let|var)\s+functionExpanded\s*=\s*applyFunction\(\s*cellExpanded\s*\);?\s*return\s*(?:functionExpanded\s*===\s*x|x\s*===\s*functionExpanded)\s*\?\s*functionExpanded\s*:\s*evalFormula\(\s*functionExpanded\s*,\s*cells\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
--fcc-editable-region--
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,164 @@
|
||||
---
|
||||
id: 646d4769ba65f1d05ef6b634
|
||||
title: Step 90
|
||||
challengeType: 0
|
||||
dashedName: step-90
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Now your `update()` function can actually evaluate formulas. Remember that you wrote the `if` condition to check that a function was called.
|
||||
|
||||
Inside your `if` statement, set the `value` of the `element` to be the result of your `evalFormula()` function. Do not pass any arguments yet.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should update the `value` property of `element` in your `if` block.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value/);
|
||||
```
|
||||
|
||||
You should assign the `value` property the result of calling your `evalFormula()` function.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(/);
|
||||
```
|
||||
|
||||
You should not pass any arguments to your `evalFormula()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*\)/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
id: 646d47c8f58107d10f1e5106
|
||||
title: Step 91
|
||||
challengeType: 0
|
||||
dashedName: step-91
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
The first argument for your `evalFormula` call needs to be the contents of the cell (which you stored in `value`). However, the contents start with an `=` character to trigger the function, so you need to pass the substring of `value` starting at index `1`.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should pass `value` as the first argument to your `evalFormula()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value/);
|
||||
```
|
||||
|
||||
You should call the `.slice()` method on the `value` argument.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(/);
|
||||
```
|
||||
|
||||
You should pass the number `1` as the argument to your `.slice()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(\s*1\s*\)\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula();
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,162 @@
|
||||
---
|
||||
id: 646d4813c17b37d1e261a566
|
||||
title: Step 92
|
||||
challengeType: 0
|
||||
dashedName: step-92
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
You can quickly get all cells from your page by getting the `#container` element by it's `id` and accessing the `children` property of the result. Pass that to your `evalFormula()` call as the second parameter.
|
||||
|
||||
# --hints--
|
||||
|
||||
For the second parameter of your `evalFormula()` call, you should call the `.getElementById()` method of the `document` object.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(\s*1\s*\)\s*,\s*document\.getElementById\(/);
|
||||
```
|
||||
|
||||
You should pass `container` as the argument to your `.getElementById()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(\s*1\s*\)\s*,\s*document\.getElementById\(\s*('|"|`)container\9\s*\)/);
|
||||
```
|
||||
|
||||
You should access the `children` property of the result of your `.getElementById()` call.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(\s*1\s*\)\s*,\s*document\.getElementById\(\s*('|"|`)container\9\s*\)\.children\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1));
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
id: 646d486aec20f7d2a581cc36
|
||||
title: Step 93
|
||||
challengeType: 0
|
||||
dashedName: step-93
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Unfortunately, that `children` property is returning a collection of elements, which is array-like but not an array. Wrap your second argument in `Array.from()` to convert it to an array.
|
||||
|
||||
# --hints--
|
||||
|
||||
You should wrap your `document.getElementById('container').children` in `Array.from()`.
|
||||
|
||||
```js
|
||||
assert.match(code, /const\s+update\s*=\s*\(?\s*event\s*\)?\s*=>\s*\{\s*const\s+element\s*=\s*event\.target;?\s*const\s+value\s*=\s*element\.value\.replace\(\s*\/\\s\/g\s*,\s*('|"|`)\1\s*\);?\s*if\s*\(\s*(!value\.includes\(\s*element\.id\s*\)\s*&&\s*(?:value\[0\]\s*===\s*('|"|`)=\3|value\.charAt\(0\)\s*===\s*('|"|`)=\4|value\.startsWith\(('|"|`)=\5\))|(?:value\[0\]\s*===\s*('|"|`)=\6|value\.charAt\(0\)\s*===\s*('|"|`)=\7|value\.startsWith\(('|"|`)=\8\))\s*\|\|\s*!value\.includes\(\s*element\.id\s*\))\s*\)\s*\{\s*element\.value\s*=\s*evalFormula\(\s*value\.slice\(\s*1\s*\)\s*,\s*Array\.from\(\s*document\.getElementById\(\s*('|"|`)container\9\s*\)\.children\s*\)\s*\);?/);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median
|
||||
}
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), document.getElementById("container").children);
|
||||
}
|
||||
}
|
||||
--fcc-editable-region--
|
||||
```
|
||||
@@ -0,0 +1,189 @@
|
||||
---
|
||||
id: 646d48b936802fd34c3f05af
|
||||
title: Step 94
|
||||
challengeType: 0
|
||||
dashedName: step-94
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Your spreadsheet is now functional. However, you don't have support for very many formulas.
|
||||
|
||||
Add an `even` property to your `spreadsheetFunctions`. It should take a `nums` parameter, and return the result of filtering the `nums` array to only include even numbers. Use a reference to your `isEven` function to help.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `spreadsheetFunctions` object should have an `even` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "even");
|
||||
```
|
||||
|
||||
Your `even` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.even);
|
||||
```
|
||||
|
||||
Your `even` function should take a `nums` parameter.
|
||||
|
||||
```js
|
||||
assert.match(code, /even\s*:\s*\(?\s*nums\s*\)?\s*=>/)
|
||||
```
|
||||
|
||||
Your `even` function should use an implicit return.
|
||||
|
||||
```js
|
||||
assert.notMatch(code, /even\s*:\s*\(?\s*nums\s*\)?\s*=>\s*\{/)
|
||||
```
|
||||
|
||||
Your `even` function should return the result of calling the `.filter()` method on `nums`.
|
||||
|
||||
```js
|
||||
assert.match(code, /even\s*:\s*\(?\s*nums\s*\)?\s*=>\s*nums\s*\.\s*filter/)
|
||||
```
|
||||
|
||||
You should pass a reference to your `isEven()` function as the callback for the `.filter()` method.
|
||||
|
||||
```js
|
||||
assert.match(code, /even\s*:\s*\(?\s*nums\s*\)?\s*=>\s*nums\s*\.\s*filter\s*\(\s*isEven\s*\)/)
|
||||
```
|
||||
|
||||
Your `even` function should return an array of even numbers.
|
||||
|
||||
```js
|
||||
assert.deepEqual(spreadsheetFunctions.even([1, 2, 3, 4, 5, 6]), [2, 4, 6]);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median,
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), Array.from(document.getElementById("container").children));
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,182 @@
|
||||
---
|
||||
id: 646d498c8ebc31d3f753b22e
|
||||
title: Step 95
|
||||
challengeType: 0
|
||||
dashedName: step-95
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Add a `firsttwo` property which takes a `nums` parameter and returns the first two elements of the `nums` array. Then add a `lasttwo` property which returns the last two elements of the `nums` array.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `firsttwo` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "firsttwo");
|
||||
```
|
||||
|
||||
Your `firsttwo` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.firsttwo);
|
||||
```
|
||||
|
||||
Your `firsttwo` function should return the first two numbers of the array.
|
||||
|
||||
```js
|
||||
assert.deepEqual(spreadsheetFunctions.firsttwo([1, 2, 3, 4, 5, 6]), [1, 2]);
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `lasttwo` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "lasttwo");
|
||||
```
|
||||
|
||||
Your `lasttwo` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.lasttwo);
|
||||
```
|
||||
|
||||
Your `lasttwo` function should return the last two numbers of the array.
|
||||
|
||||
```js
|
||||
assert.deepEqual(spreadsheetFunctions.lasttwo([1, 2, 3, 4, 5, 6]), [5, 6]);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median,
|
||||
even: nums => nums.filter(isEven),
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), Array.from(document.getElementById("container").children));
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,190 @@
|
||||
---
|
||||
id: 646d49bfff9079d4b38df115
|
||||
title: Step 96
|
||||
challengeType: 0
|
||||
dashedName: step-96
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Add a `has2` property which returns whether the `nums` array has `2` in the values, and an `increment` property which returns `nums` with every value incremented by one.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `has2` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "has2");
|
||||
```
|
||||
|
||||
Your `has2` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.has2);
|
||||
```
|
||||
|
||||
Your `has2` function should return `true` if the array has `2` in it.
|
||||
|
||||
```js
|
||||
assert.isTrue(spreadsheetFunctions.has2([1, 2, 3]));
|
||||
```
|
||||
|
||||
Your `has2` function should return `false` if the array does not have `2` in it.
|
||||
|
||||
```js
|
||||
assert.isFalse(spreadsheetFunctions.has2([1, 3, 4]));
|
||||
```
|
||||
|
||||
Your `spreadsheetFunctions` object should have an `increment` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "increment");
|
||||
```
|
||||
|
||||
Your `increment` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.increment);
|
||||
```
|
||||
|
||||
Your `increment` function should return an array of numbers incremented by one.
|
||||
|
||||
```js
|
||||
assert.deepEqual(spreadsheetFunctions.increment([1, 2, 3]), [2, 3, 4]);
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median,
|
||||
even: nums => nums.filter(isEven),
|
||||
firsttwo: nums => nums.slice(0, 2),
|
||||
lasttwo: nums => nums.slice(-2),
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), Array.from(document.getElementById("container").children));
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,183 @@
|
||||
---
|
||||
id: 646d4a07a8fb14d55cd70e09
|
||||
title: Step 97
|
||||
challengeType: 0
|
||||
dashedName: step-97
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Arrays have a `.some()` method. Like the `.filter()` method, `.some()` accepts a callback function which should take an element of the array as the argument. The `.some()` method will return `true` if the callback function returns `true` for at least one element in the array.
|
||||
|
||||
Here is an example of a `.some()` method call to check if any element in the array is an uppercase letter.
|
||||
|
||||
```js
|
||||
const arr = ["A", "b", "C"];
|
||||
arr.some(letter => letter === letter.toUpperCase());
|
||||
```
|
||||
|
||||
Add a `someeven` property to your `spreadsheetFunctions` - use the `.some()` method to check if any element in the array is even.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `someeven` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "someeven");
|
||||
```
|
||||
|
||||
Your `someeven` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.someeven);
|
||||
```
|
||||
|
||||
Your `someeven` function should return `true` if some of the elements in the array are even.
|
||||
|
||||
```js
|
||||
assert.isTrue(spreadsheetFunctions.someeven([1, 2, 3]));
|
||||
```
|
||||
|
||||
Your `someeven` function should return `false` if none of the elements in the array are even.
|
||||
|
||||
```js
|
||||
assert.isFalse(spreadsheetFunctions.someeven([1, 3, 5]));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median,
|
||||
even: nums => nums.filter(isEven),
|
||||
|
||||
firsttwo: nums => nums.slice(0, 2),
|
||||
lasttwo: nums => nums.slice(-2),
|
||||
has2: nums => nums.includes(2),
|
||||
increment: nums => nums.map(num => num + 1),
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), Array.from(document.getElementById("container").children));
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
---
|
||||
id: 646d4a5b32a1cad6165df286
|
||||
title: Step 99
|
||||
challengeType: 0
|
||||
dashedName: step-99
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Add a `random` property which takes the first two numbers from an array and returns a random number between them. Use the `Math.random()` function to help.
|
||||
|
||||
# --hints--
|
||||
|
||||
Your `spreadsheetFunctions` object should have a `random` property.
|
||||
|
||||
```js
|
||||
assert.property(spreadsheetFunctions, "random");
|
||||
```
|
||||
|
||||
Your `random` property should be a function.
|
||||
|
||||
```js
|
||||
assert.isFunction(spreadsheetFunctions.random);
|
||||
```
|
||||
|
||||
Your `random` function should return a random number between the first two numbers in the array.
|
||||
|
||||
```js
|
||||
assert.isAtLeast(spreadsheetFunctions.random([1, 2, 3]), 1);
|
||||
assert.isAtMost(spreadsheetFunctions.random([1, 2, 3]), 2);
|
||||
```
|
||||
|
||||
Your `random` function should return a whole number (integer).
|
||||
|
||||
```js
|
||||
const result = spreadsheetFunctions.random([1, 10]);
|
||||
assert.equal(result, Math.floor(result));
|
||||
```
|
||||
|
||||
# --seed--
|
||||
|
||||
## --seed-contents--
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="./styles.css" />
|
||||
<title>Functional Programming Spreadsheet</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="./script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
```css
|
||||
#container {
|
||||
display: grid;
|
||||
grid-template-columns: 50px repeat(10, 200px);
|
||||
grid-template-rows: repeat(11, 30px);
|
||||
}
|
||||
|
||||
.label {
|
||||
background-color: lightgray;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 30px;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
const infixToFunction = {
|
||||
"+": (x, y) => x + y,
|
||||
"-": (x, y) => x - y,
|
||||
"*": (x, y) => x * y,
|
||||
"/": (x, y) => x / y,
|
||||
}
|
||||
|
||||
const infixEval = (str, regex) => str.replace(regex, (_match, arg1, operator, arg2) => infixToFunction[operator](parseFloat(arg1), parseFloat(arg2)));
|
||||
|
||||
const highPrecedence = str => {
|
||||
const regex = /([\d.]+)([*\/])([\d.]+)/;
|
||||
const str2 = infixEval(str, regex);
|
||||
return str === str2 ? str : highPrecedence(str2);
|
||||
}
|
||||
|
||||
const isEven = num => num % 2 === 0;
|
||||
const sum = nums => nums.reduce((acc, el) => acc + el, 0);
|
||||
const average = nums => sum(nums) / nums.length;
|
||||
|
||||
const median = nums => {
|
||||
const sorted = nums.slice().sort((a, b) => a - b);
|
||||
const length = sorted.length;
|
||||
const middle = length / 2 - 1;
|
||||
return isEven(length)
|
||||
? average([sorted[middle], sorted[middle + 1]])
|
||||
: sorted[Math.ceil(middle)];
|
||||
}
|
||||
|
||||
--fcc-editable-region--
|
||||
const spreadsheetFunctions = {
|
||||
sum,
|
||||
average,
|
||||
median,
|
||||
even: nums => nums.filter(isEven),
|
||||
someeven: nums => nums.some(isEven),
|
||||
everyeven: nums => nums.every(isEven),
|
||||
firsttwo: nums => nums.slice(0, 2),
|
||||
lasttwo: nums => nums.slice(-2),
|
||||
has2: nums => nums.includes(2),
|
||||
increment: nums => nums.map(num => num + 1),
|
||||
|
||||
}
|
||||
--fcc-editable-region--
|
||||
|
||||
const applyFunction = str => {
|
||||
const noHigh = highPrecedence(str);
|
||||
const infix = /([\d.]+)([+-])([\d.]+)/;
|
||||
const str2 = infixEval(noHigh, infix);
|
||||
const functionCall = /([a-z]*)\(([0-9., ]*)\)(?!.*\()/i;
|
||||
const toNumberList = args => args.split(",").map(parseFloat);
|
||||
const apply = (fn, args) => spreadsheetFunctions[fn.toLowerCase()](toNumberList(args));
|
||||
return str2.replace(functionCall, (match, fn, args) => spreadsheetFunctions.hasOwnProperty(fn.toLowerCase()) ? apply(fn, args) : match);
|
||||
}
|
||||
|
||||
const range = (start, end) => Array(end - start + 1).fill(start).map((element, index) => element + index);
|
||||
const charRange = (start, end) => range(start.charCodeAt(0), end.charCodeAt(0)).map(code => String.fromCharCode(code));
|
||||
|
||||
const evalFormula = (x, cells) => {
|
||||
const idToText = id => cells.find(cell => cell.id === id).value;
|
||||
const rangeRegex = /([A-J])([1-9][0-9]?):([A-J])([1-9][0-9]?)/gi;
|
||||
const rangeFromString = (num1, num2) => range(parseInt(num1), parseInt(num2));
|
||||
const elemValue = num => character => idToText(character + num);
|
||||
const addCharacters = character1 => character2 => num => charRange(character1, character2).map(elemValue(num));
|
||||
const rangeExpanded = x.replace(rangeRegex, (_match, char1, num1, char2, num2) => rangeFromString(num1, num2).map(addCharacters(char1)(char2)));
|
||||
const cellRegex = /[A-J][1-9][0-9]?/gi;
|
||||
const cellExpanded = rangeExpanded.replace(cellRegex, match => idToText(match.toUpperCase()));
|
||||
const functionExpanded = applyFunction(cellExpanded);
|
||||
return functionExpanded === x ? functionExpanded : evalFormula(functionExpanded, cells);
|
||||
}
|
||||
|
||||
window.onload = () => {
|
||||
const container = document.getElementById("container");
|
||||
const createLabel = (name) => {
|
||||
const label = document.createElement("div");
|
||||
label.className = "label";
|
||||
label.textContent = name;
|
||||
container.appendChild(label);
|
||||
}
|
||||
const letters = charRange("A", "J");
|
||||
letters.forEach(createLabel);
|
||||
range(1, 99).forEach(number => {
|
||||
createLabel(number);
|
||||
letters.forEach(letter => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "text";
|
||||
input.id = letter + number;
|
||||
input.ariaLabel = letter + number;
|
||||
input.onchange = update;
|
||||
container.appendChild(input);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const update = event => {
|
||||
const element = event.target;
|
||||
const value = element.value.replace(/\s/g, "");
|
||||
if (!value.includes(element.id) && value.startsWith('=')) {
|
||||
element.value = evalFormula(value.slice(1), Array.from(document.getElementById("container").children));
|
||||
}
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user