I'm Matt - You're here for my blog!

Category: JavaScript

ES/TC39 Proposals – Change Array by copy (Stage 3) – Immutable Array Operations

As a follow up to my last post, I’ve decided to dig in a little more to some of the upcoming TC39 proposals. The proposal that we’re reviewing today provides a few new ways to work with Arrays in an immutable way – a proposal titled “Change Array by copy”.

As the title “Change Array by copy” suggests, the new methods covered by the proposal provide Array functions which allow working with arrays in an immutable fashion (with the assumed intent of reducing side effects often associated with mutable objects, which are capable of being directly changed).

Included under the proposal are:

  • Array.prototype.toSorted()
  • Array.prototype.toReversed()
  • Array.prototype.toSpliced()
  • Array.prototype.with()

You can check out the collaborative view of this proposal on GitHub.

Let’s dive in!

Similarly to Array.prototype.sort(), Array.prototype.toSorted() exposes a helper function capable of sorting members of an array sequentially. Unlike to Array.protptype.sort(), Array.prototype.toSorted() returns a new array, by copy, leaving the original array untouched. To see this in action, see the following example:

Array.prototype.sort() vs Array.prototype.toSorted() – As expected, arr1 is changed, while arr2 is not.

Up next, we have Array.prototype.toReversed() – essentially, the immutable form of Array.prototype.reverse(). Just as reverse() reverses the member order of the array, so to does toReversed() – but, again, without modifying the source array being operated on/against.

Array.prototype.reverse() vs Array.prototype.toReversed() – As expected, arr1 is changed, while arr2 is not.

Last in our one-to-one function comparison is Array.prototype.toSpliced() – similar to the long standing and familiar Array.prototype.splice(), which is used to remove or replace elements within an array. As we can see in the following example, we’re capable of removing and replacing array elements with both splice() and toSpliced() – with the distinction that toSpliced() doesn’t modify the original array, and instead provides us a new, updated one via copy.

Array.prototype.splice() vs Array.prototype.toSpliced() – As expected, arr1 is changed, while arr2 is not.

And finally, we have Array.prototype.with(). Unlike the previously discussed parts of the proposal, with() doesn’t really have a similarly named existing function – though we’re all likely familiar with the logic in play. When working with an array, it’s often common practice to reassign a value within an array by assigning a new value using the target’s zero-based index – with() exposes exactly this functionality, via a function call. See the following example to see Array.prototype.with() in play:

Array index-based assignment vs Array.prototype.with() – as expected, arr1 is changed, while arr2 is not.

As you may have noticed in the above (and in case you did not) we’re once again using the core-js library to play with these new, not-yet-spec, JavaScript proposals – so check it out, and perhaps give it a spin!

Anywhos… I’m really enjoying having the opportunity to look forward and actually play with new stuff in the works for JavaScript. Yay, funemployment! That said, if you or someone you know is seeking a developer with well over a decade of full stack experience, including large enterprise engineering and architecture, please do reach out via matt@immmatt.com or ping me on Twitter @mattezell!

Happy Coding!
-Matt

ES/TC39 Proposals – Playing With Tomorrow’s JavaScript Today (core-js)

tl;dr

  • Trying to learn more
  • Ran across a JavaScript feature proposal for Array.prototype.group()
  • Peaked behind the curtain of the future of JavaScript by way of TC39
  • Learned of core-js (‘npm install core-js’)
  • Brought Array.prototype.group into my Nodejs module project via core-js (“import group from ‘core-js’;”)
  • Played with Tomorrow’s JavaScript today!

Still want to read more? Great!

I’ve been a JavaScript developer…well…for about as long as I’ve been a developer at all. That said, and admittedly a little embarrassingly, I’m far from an expert on the language – but I’m learning more everyday, including more about how little I know.

As if often the case with developers, you complete school and you quickly realize that you need a job. Armed with a bunch of CS theory, and what’s basically a beginners level of experience, you pick up new skills and tricks as you need them along the way – but rarely is there sufficient time to learn much else outside of this while maintaining balance in other parts of your life…

This is the process for years for many/most developers – you show up for work, you’re given a feature to implement, you encounter something you don’t know how to do, you do a little research, you try a few things until you find something that works, and then you move on – rarely thinking about it again. As the years go on, you keep doing that thing that you found that worked – but rarely do most of us dig in deeper to understand more or to find alternatives. Again, there’s unfortunately only so many hours in a day, week, month, year, and lifetime – and there’s a lot to living outside of punching a keyboard.

But I digress… I’m trying to be better – I’m trying to be proactive… I’m trying to dig deeper, to gain a greater understanding and to perhaps learn some alternative approaches to old problems along the way.

In my recent adventures of funemployment, I decided to play around more free-form in JavaScript… I find the MDN Web docs to be an excellent resource for JavaScript, which is where I began my journey of playing with experimental JavaScript features/proposals today. I figured “let’s get back to the basics” and take a look at the standard built-in objects, which is when I ran across the experimental proposal Array.prototype.group() – that cute little experimental beaker icon just screamed out to me.

Consider the following…

const inventory = [
  { name: "asparagus", type: "vegetables", quantity: 5 },
  { name: "bananas", type: "fruit", quantity: 0 },
  { name: "goat", type: "meat", quantity: 23 },
  { name: "cherries", type: "fruit", quantity: 5 },
  { name: "fish", type: "meat", quantity: 22 },
];

const result = inventory.group(({ type }) => type);

/* Result is:
{
  vegetables: [
    { name: 'asparagus', type: 'vegetables', quantity: 5 },
  ],
  fruit: [
    { name: "bananas", type: "fruit", quantity: 0 },
    { name: "cherries", type: "fruit", quantity: 5 }
  ],
  meat: [
    { name: "goat", type: "meat", quantity: 23 },
    { name: "fish", type: "meat", quantity: 22 }
  ]
}
*/

We have an array of JSON objects named ‘inventory’ – with standardized properties associated with each contained object. As we can see, calling array.group() passing in ‘type’ as our associative element – conceptually, we can think of this as a category. As such, we can see the resulting output of our group() call is an object comprised of our ‘type categories’, which are arrays containing the matching type objects.

I thought this was pretty cool and useful, so I wanted to try it out… Naively, I thought “Nodejs always has bleeding edge stuff – so let’s just plop in our playground code…”.

D:\w\nodejs\csv.js:45
jsonArr.group((country => country));
        ^

TypeError: jsonArr.group is not a function
    at Object.<anonymous> (D:\w\nodejs\csv.js:45:9)
    at Module._compile (node:internal/modules/cjs/loader:1159:14)
    at Module._extensions..js (node:internal/modules/cjs/loader:1213:10)
    at Module.load (node:internal/modules/cjs/loader:1037:32)
    at Module._load (node:internal/modules/cjs/loader:878:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:23:47

Node.js v18.12.1

Whelp… That’s not going to get it – Array.prototype.group() doesn’t exist… Why? Because it’s not part of JavaScript today – it’s a proposal for the future… Time to grow that brain! How might one, if so inclined, learn about these experimental JavaScript features and proposals?

Enter TC39 – “a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript” (from the official website). Cool. So, let’s take a peak at the stage 3 draft proposal for Array Grouping on Github.

It’s from the proposal that a whole new world of experimentation was shown to me… Down at the bottom of the proposal, I notice a link to an external polyfill library – ‘core-js‘… Wait… Does this mean I get to play with the future of JavaScript today?! Yup!

This is all new to me, so I figured I should just start throwing stuff at the wall to see what sticks… Up first, let’s just install…

npm install --save core-js

Now let’s figure out how to use this bad boy… Since I have my Nodejs project setup as a module, I figure I will just try to import group. Note: I’m not saying this is the best way to go about this, or anything of the sort – just sharing my process of discovery in case there’s interest.

import group from 'core-js';

So far, so good.

Now, consider my project… It’s a basic utility script – imports a CSV, and then coerces the CSV data into JSON array containing objects that represent said data…

Here’s my CSV contents:

name;role;country
Sarene;Help Desk Operator;Thailand
Olvan;Nurse Practicioner;China
Janos;Cost Accountant;China
Dolph;Assistant Manager;China
Ariela;Database Administrator I;Azerbaijan
Lane;Environmental Tech;Indonesia
Griselda;Senior Quality Engineer;Portugal
Manda;Physical Therapy Assistant;Brazil
Leslie;Information Systems Manager;Japan
Aleen;Cost Accountant;Canada

Here’s my main coercion function and call to it (assume we’ve already read in and cleaned up our CSV):

function csvTxtToJsonArr(csvTxt, delimiter = ',') {
    const headers = csvTxt.slice(0, csvTxt.indexOf('\n')).split(delimiter);
    const rowStrs = csvTxt.slice(csvTxt.indexOf('\n') + 1).split('\n');
    const jsonArr = rowStrs.map((currRowStr) => {
        const rowVals = currRowStr.split(delimiter);
        const mapObj = headers.reduce((newObj, currHeader, idx) => {
            newObj[currHeader] = rowVals[idx];
            return newObj;
        }, {});
        return mapObj;
    });
    return jsonArr
}

const jsonArr = csvTxtToJsonArr(csvContents, ';');

Which results in a jsonArr that looks like this:

[
  { name: 'Sarene', role: 'Help Desk Operator', country: 'Thailand' },
  { name: 'Olvan', role: 'Nurse Practicioner', country: 'China' },
  { name: 'Janos', role: 'Cost Accountant', country: 'China' },
  { name: 'Dolph', role: 'Assistant Manager', country: 'China' },
  {
    name: 'Ariela',
    role: 'Database Administrator I',
    country: 'Azerbaijan'
  },
  { name: 'Lane', role: 'Environmental Tech', country: 'Indonesia' },
  {
    name: 'Griselda',
    role: 'Senior Quality Engineer',
    country: 'Portugal'
  },
  {
    name: 'Manda',
    role: 'Physical Therapy Assistant',
    country: 'Brazil'
  },
  {
    name: 'Leslie',
    role: 'Information Systems Manager',
    country: 'Japan'
  },
  { name: 'Aleen', role: 'Cost Accountant', country: 'Canada' }
]

Cool… I guess… I mean, just basic, boring CSV play… But group – yeah, Array.prototype.group() seems like it might be fun here… So, let’s try it out using country as a grouping element:

jsonArr.group(({country}) => country);

Which gives us the following results:

{
  Thailand: [
    { name: 'Sarene', role: 'Help Desk Operator', country: 'Thailand' }
  ],
  China: [
    { name: 'Olvan', role: 'Nurse Practicioner', country: 'China' },
    { name: 'Janos', role: 'Cost Accountant', country: 'China' },
    { name: 'Dolph', role: 'Assistant Manager', country: 'China' }
  ],
  Azerbaijan: [
    {
      name: 'Ariela',
      role: 'Database Administrator I',
      country: 'Azerbaijan'
    }
  ],
  Indonesia: [
    { name: 'Lane', role: 'Environmental Tech', country: 'Indonesia' }
  ],
  Portugal: [
    {
      name: 'Griselda',
      role: 'Senior Quality Engineer',
      country: 'Portugal'
    }
  ],
  Brazil: [
    {
      name: 'Manda',
      role: 'Physical Therapy Assistant',
      country: 'Brazil'
    }
  ],
  Japan: [
    {
      name: 'Leslie',
      role: 'Information Systems Manager',
      country: 'Japan'
    }
  ],
  Canada: [ { name: 'Aleen', role: 'Cost Accountant', country: 'Canada' } ]
}

Cool! Right?!

Will Array.prototype.group() become standard spec? I can’t say, but it looks promising… But I suppose what I wanted to share more than this cool new group proposal was how you and I can be a little more hands on with what’s potentially to come to the future of JavaScript by way of the handy-dandy core-js library!

I feel that it’s worth mentioning another interesting and useful resource I ran across while playing this morning… The unofficial ES Proposals site, which is a labor of love from Apple Software Engineer Saad Quadri. Check it out!

Anywhos… Happy coding!
-Matt

Exploring Denoland – Home of the Deno JavaScript Runtime

With some of my newly found ‘free time’ while Funemployed, I really wanted to start playing around with some of the newer projects that I’ve simply not had the time or energy for.

At the top of this list has been Deno, the new-ish JavaScript, TypeScript and WebAssembly runtime based on V8, co-created by Ryan Dahl (Node.js creator).

Why Deno? Well, for starters I love new and shiny things. But also, I have a special place in my heart for JavaScript runtimes – having been an early adopter of Node.js, at a time when serverside JavaScript was a controversial topic in the dev world (Thanks, Whiteboard IT! It’s because of my time with this company that I was exposed to a lot of really cool things, including Node.js and CouchDB/NoSQL). “How early?”, one might ask… Early enough that I’m listed as contributor #306 in the Node.js AUTHORS file – as a result of contributing a slash to the first official Windows installer.

I began my journey with Deno as I normally do – essentially iterating on some basic Hello World types of projects to get familiar with the dev flow, tooling and capabilities. Pretty quickly, I encountered something in my codebase that confused me – a warning notifying me that the deno window typings didn’t have everything supported by window exposed. Surprisingly, it wasn’t just me randomly plugging in code that landed me here – it was in following along with the Deno lifecycle docs that brought me to this error.

“Element implicitly has an ‘any’ type because type ‘typeof globalThis’ has no index signature.deno-ts(7017)”

At this point in my journey, not only was I getting the warning in VSCode, but I was also unable to get beforeunload or onbeforeunload to fire… Hmm… So off to Google I go – which lead me to this thread discussing a related matter.

In digging a bit deeper, I came to realize that beforeunload/onbeforeunload weren’t firing due to my Deno version – with support for these not being added to window in Deno until v1.24.0 (and me currently running v1.23.0). “Alright”, I thought – “I just need to upgrade and this will all be solved.”

Sure enough, once on 1.24.0 I was able to play with beforeunload/onbeforeunload – but the “Element implicitly has an ‘any’ type because type ‘typeof globalThis’ has no index signature.deno-ts(7017)” warning remained…

Having wished I had more time to contribute to open source over the last several years, I saw this as an opportunity to make my wish a reality – and so I did what any developer would do, created a fork and started to work on a new branch in the hopes I could become a Deno contributor!

As most experienced devs know, stepping into a large and complex codebase isn’t always the easiest thing to do – especially if you’ve never worked with some of the core tooling. With this being my first real exposure to Rust, I was completely unfamiliar with Cargo and how to go about building and testing my changes in an efficient manner… Fortunately, after a handful of web searches, I learned the basics of working in Rust and with Cargo, how to run specific tests by name, and I was off to the races to become a real life Deno contributor.

My commit is simple enough – really just a few lines to add beforeunload and onbeforeunload to the type definition for Deno window, and some updated integration tests to ensure everything is working as expected – you can see the pull request / merge here. While a small contribution by pretty much all standards of measurement, it felt good to see an issue, fix the issue and contribute that fix back to the community – and I hope to be doing more of this in the near future.

So, what’s next? Well, I guess I can actually begin with what I set out to do – learn Deno (rather than Deno’s codebase) and start playing with Fresh! 😁

Happy Coding!
-Matt

© 2024 blog.immatt

Theme by Anders NorenUp ↑