Jon Rumsey

An online markdown blog and knowledge repository.


Project maintained by nojronatron Hosted on GitHub Pages — Theme by mattgraham

Nojronablog 2024

A space for collecting thoughts and technical walk-thrus and takeaways during my coding journey through CY 2024.

Weeks 39 through 40

Multiple events the last few weeks have caused some disruption in my development cycles, note taking, learning cycles, etc. Also, jury duty calls, which might suddenly interrupt and cause uneven productivity here.

Markdown ToC VS Code Extension

Next version is nearly ready to publish, after working through and implementing the logic to support Closed ATX and Alternate Style headings, performing some refactorings, updating unit tests, and validating readiness through manual tests!

Some key takeaways and things I said to myself (and out loud) while working through this project:

I completed publishing a Release version of Create-ToC set at version 0.4.2. Pretty much right after publishing I discovered a few bugs. I need to update my development processes to be certain the following steps are completed:

Doing these things will help keep my workflow organized, even when I have to step away from the project for some time between bugfixes and version releases.

After a bit more debugging, some code refinements, added documentation, unittest fixes, and adding manual-testing files, version 0.4.3 is now available as a Pre-Release version. In a few days, after some regular usage, I'll Publish a 0.4.4 Release version. :tada:

Markdown ToC Extension Internal Operation as of Sept 26th

While publishing the pre-release at 0.4.3, I wanted to document some of the operation of the code for personal purposes.

  1. VS Code API registers markdown-toc.createTOC and loads an anonymous async function as the second parameter, setting both as disposable objects.
  2. A copy of the Editor object is acquired from the VSCode API and it is validated as a 'markdown' type document. If not an MD file a WarningMessage is displayed and execution returns null.
  3. The Editor object is scanned to get a count of text lines. If there are too few, a WarningMessage is displayed and execution returns null.
  4. Function findTopHeading() is executed, and the results are stored in an object to identify the Heading style (Open ATX, Closed ATX, or Next Line) as well as the Line Number the top (Level 1) heading is found.
  5. If the Top Heading is not found, a WarningMessage is displayed and execution returns null.
  6. A RegExp match() function is called to check for an existing Table of Contents. If there is at least one match, a Warning Message is displayed and execution returns.
  7. If there are too few lines of text remaining after the Top Heading line number, a WarningMessage is displayed and execution returns null.
  8. Function getLevelHeading() is called within a for code block and positive results are stored in a local array. Inner-functions getTitleOnly(), which replaces illegal Heading title characters, and either getHash2LH() or getDash2LH() are executed (depending on the style) to acquire the correct Level 2 Headings, ignoring any other text or headings levels.
  9. After the For loop exits, if there are no items in the array, a WarningMessage is displayed and execution returns null.
  10. Function createTOC() is called, which in turn calls getTitleOnly() which replaces illegal Heading title characters, and then getLoweredKebabCase() which (as its name states) forces lower-cased characters and replaces any whitespace characters with a dash - (except for newline and carriage return, which are ignored). Lastly, function getLinkFragment() is called which properly formats the Title and Lowered Kebab Case outputs into an appropriate (lint-able) Link Fragment.
  11. The result variable from getTitleOnly() is fed into a VS Code API edit.insert() function, which adds the formatted string data to the active document.
  12. The VS Code API workspace.applyEdit() function is called to 'write' the formatted string data (the new Table of Contents) to the working document so the user can see it and save it.
  13. A WarningMessage is displayed indicating the table of contents has been created, and the anonymous function exits, returning null.
  14. Function push() is called on API ExtensionContext to add the disposable veriable to the ExtensionContexts.subscriptions Array.

The 0.4.4 release is now Published to the VS Code Extension Marketplace! :tada:

RegEx and the Multiline Setting

const regexpPattern1 = ...; // some pattern
const regexpPattern2 = ...; // some other pattern

// might be easier to read
const pairedOrLogic = inputText.match(/^regexpPattern1$/gm) !== null 
                   || inputText.match(/^regexpPattern2$/gm) !== null;

// might be more succinct
const groupedMatches = inputText.match(/^(?:regexpPattern1|regexpPattern2)$/gm) !== null;

// 1) using `^` and `$` along with `/gm` could cause the regexp to consume more resources than desired
// 2) optionally use a quick-exit technique with `/m` so string.match(regexp) returns after first match
return inputText.match(/^(?:regexPattern1|regexPattern2)$/m) !== null;

But working with RegEx is very tricky and there are many ways to approach pattern matchine. Here are some questions to ask while figuring out RegEx patterns:

Portfolio Website Updates

I took time to update My Portfolio Web Site:

The original project used bootstrap tooling CRA (Create React App) which is known to have some limitations and is also fairly stagnant, so I decided to challenge myself and move to Vite tooling instead.

Getting the Web Site to deploy to Netlify using Vite was only slightly challenging:

Note: Ubuntu released 24.x (Desktop, Server, etc) as an LTS version, but Netlify's JamStack is pinned to Ubuntu Focal 20.04 which appears to be an LTS release with general support until May 2025, and ESM (Extended Security Support) until 2030. So there is a good chance Netlify will make (hopefully) Ubuntu Noble 24.x LTS sometime in the coming months.

Many more updates are in the works, providing opportunity for me to learn and grow my website building skill sets.

I completed the tasks for a Pull Request with major updates and deployed the site without issue. There is still plenty to do (especially in terms of accessibility) and my plan is to take on these issues over time and incrementally improve the site UX for site visitors, and to keep myself plugged-in and working with React and webapp building and maintenance.

Weeks 35 through 38

So many events, so little time!

As I have had time, I've attended some online informational sessions about AI and DotNET, worked on updates to my Create-ToC VS Code Extension, and make some connections with other developers and a couple of organizations that might utilize my technical skills. There is a lot going on right now between learning, volunteering, networking, coding, and life in general.

To Redesign Or Massively Refactor

That is the question. While working through updating my Markdown ToC extension, it became clear that my design suffers from difficulty in testing and extending. The latest push has been to enable Create-ToC to recognize both Open ATX and Closed ATX headings styles, and follow-suite when generating the Table of Contents.

Some refactoring of implementations into modules, and removing extraneous module functions results in a more testable, and simplified implementation. Unit tests were also refactored to test the updated JS Modules and their functions.

But there's more work to do! Unit tests are still failing, so those issues need to be worked out, and once that's done the README documentation must be updated, and the version incremented for publication. This time, I want to publish the next version as a full release, rather than a pre-release (as I did earlier this year). I'm looking forward to having an updated functional utility in a public marketplace!

Week 29 through 34

For much of week 29, I was out of pocket not feeling well so not many updates were made during this time.

During weeks 30 through 34, I had several events and many meetings to attend to. There is not as much to report, but some summaries are included below.

Updating and Releasing BF Bib Report Form v2

During the massive merging party, preparing for the latest 2.x release of the form, a few functions were not well tested enough to know they were incorrectly implemented. I'm pretty sure this was a result of interrupted development that was not followed-up and validated properly. This required pushing some quick-fix commits.

Key takeaways here:

BF-BMX Reporting Exploration

Recently I started looking into getting the BF-BMX API Server to return information on what it has stored in its DB. At first this was exploratory, but during the last week or so I have turned a corner in my thinking and decided to develop a preview of a Reporting Server that will simply render information about the stored data.

The following items are a rought overview of the remaining work I'd like to get done before this year's event:

This is really exciting to me and I look forward to having this tool to keep on top of participant data at this and future events!

Full Stack Querying With Blazor

I've just completed several days of focused work on the BF-BMX Reporting Service, and it is looking and working fairly well. There are still some bugs and nits that need to get addressed, but for the most part the solution is ready for this year's event, and I'm starting to trust the data displays it is rendering.

Some key takeaways, highlights, and lowlights of the last few days:

More RaspberryPi Fun

A recent conversation with a ham friend resulted in a renewed interest in computer networks and automation, so I've been looking into Kismet Wireless and working with Linux cron and registering custom services. I'm already planning to use RPi's at a few upcoming ham events, so I'll integrate some of what I learn into building and configuring Pis.

Antenna Maintenance

A couple weeks ago I replaced my VHF omni vertical with a VHD beam antenna. The omni antenna is better in windy or icy conditions so I tend to have it up during the darker months, but there is a local RF problem (reflections or some other RF emitter) and the omni receives those all too well. The yagi is able to avoid those noise issues with a more focused view, and the rotator allows changing direction remotely. However, I haven't tuned the yagi since I last put it back together, so I'll need to run some diagnostics to find out if the tuning is out of band, and make requisite changes.

Last week I did maintanance on my HF antenna and followed-up with some experimentation to try and improve its performance on many bands that I want to use. It turns out my previous installation using a 9:1 unun with a 80-ish foot hot wire and a 25 foot ground wire was not a great solution. I've been using it for years, but had to be careful about what bands and modes I used due to poor tuning in multiple areas. After experimenting and reading more about off-center-fed dipoles and end-fed long wire antennas, I decided my OCF implementation was faulty. So the antenna was refactored to follow advice from Palomar Engineers by shrinking the main radiating element and removing the ground element completely. Now the antenna covers more bands than before, and performs better on sub-bands I wanted. </otherstuff>

Build 2024 Catch-up Views in July

I took some time out to review some missed Build 2024 sessions, and updated documentation accordingly.

DotNET Aspire Day 2024

Microsoft Reactor is hosting an online ".NET Aspire Developers Day" where multiple speakers will discuss and demonstrate .NET Aspire use cases and implementation details. See DotNET Aspire for notes.

Bigfoot 2024

The event was fun an exciting, but multiple twists made for a very different experience this year. The BF-BMX tool came in handy at my location, especially with the BFBMX.Reports tool that I had started developing in July:

In the near future, a meeting will be scheduled to discuss BF-BMX performance, usability, and planning for v2. This will probably wait until October, given how busy September is shaping up to be.

VSCode Extension: Table of Contents

In July I put some effort into implementing a bugfix and new feature, and completed a preview version publication. Unfortunately, it is not ready for full release yet. I have a work item in my backlog that will be promoted forward to fix and increment the pre-release version, and implement another new feature for a next minor version Preview and release.

Internally I have a goal to get the extension into a capable, reliable state before the end of 2024. I use the tool almost every day that I code, so having a stable, helpful tool that I built myself is really rewarding!

Mid August Leetcode Challenges

I manage to complete a couple Leetcode challenges:

  1. Merge two sorted singly-linked lists.
  2. Remove duplicates from sorted array.

It has been long enough since I last worked on a Linked-List DS&A challenge that I really had a hard time completing the first challenge. After throwing around some ideas, and attempting to implement them, I had to stop what I was doing, reconsider what I think I know about Singly Linked Lists, and start over before getting the solution.

Removing duplicates from a sorted Integer array wasn't too difficult. Early on I recalled how to utilize a HashSet to maintain a unique collection, and managed to get a solution working within a single iteration over the input array. The final solution (and the best performing) was one that borrowed ideas from sorting algorithms where only the indices were tracked, and when certain conditions are met either one or both indices are incremented, or the value from the right index would be used to overwrite the value at the left index. This made a big difference in performance and code simplicity and readability.

Merge Two Sorted Singly-Linked Lists:

Function: MergeTwoLists
Input: ListNode LeftList, ListNode RightList
Output: ListNode

Instantiate: ListNode OutputNode <- new
If: LeftList EQ Null
  Reassign: OutputNode <- RightList
  Return: OutputNode
Else If: RightList EQ Null
  Reassign: OutputNode <- LeftList
  Return: OutputNode
If: LeftList Value LE RightList Value
  Reassign: OutputNode <- new ListNode <- LeftList Value
  Reassign: LeftList <- LeftList Next
Else:
  Reassign: OutputNode <- new ListNode <- RightList Value
  Reassign: RightList <- RightList Next
Initialize: ListNode OutputTail <- OutputNode
While: TRUE
  If: LeftList NOT Null AND RightList NOT Null
    Switch on Comparison: LeftList Value, RightList Value
      Case: -1
        Reassign: OutputTail Next <- new ListNode <- LeftList Value
        Reassign: LeftList <- LeftList Next
      Case: 1
        Reassign: OutputTail Next <- new ListNode <- RightList Value
        Reassign: RightList <- RightList Next
      Case: 0
        Reassign: OutputTail Next <- new ListNode <- LeftList Value
        Reassign: OutputTail <- OutputTail Next
        Reassign: LeftList <- LeftList Next
        Reassign: OutputTail Next <- new ListNode <- RightList Value
        Reassign: RightList <- RightList Next
    Reassign: OutputTail <- OutputTail Next
  Else If: LeftList NOT Null
    Reassign: OutputTail Next <- new ListNode <- LeftList Value
    Reassign: OutputTail <- OutputTail Next
    Reassign: LeftList <- LeftList Next
  Else If: RightList NOT Null
    Reassign: OutputTail Next <- new ListNode <- RightList
    Reassign: OutputTail <- OutputTail Next
    Reassign: RightList <- RightList Next
  If: RightList Null AND LeftList Null
    Execute: Break
Return: OutputNode

Note: The Switch-Case block ignores the C# rule that a Default statement should be last. Other languages might not require this, so for simplicity of writing pseudocode I skipped it.

Remove Dupes From Sorted Array

Function: RemoveDuplicates
Input: NumsArray
Output: NumsCount

If: NumsArray Length GT 2
  Return: NumsArray Length
Initialize: LeftIdx <- 0
Initialise: RightIdx <- 0
While: LeftIdx LT NumsArray Length
  If: LeftIdx EQ RightIdx
    Reassign: RightIdx <- Increment 1
    Continue: (next iteration)
  If: NumsArray at Index LeftIdx EQ NumsArray at Index RightIdx
    Reassign: RightIdx <- Increment 1
  Else:
    Reassign: LeftIdx <- Increment 1
    Reassign: NumsArray at Index LeftIdx <- NumsArray at Index RightIdx
Reassign: LeftIndex <- Increment 1
Return: LeftIndex

Just for the record, I only write these solutions out as practice:

It is up to readers of this rambling blog to do the right things.

Week 28

Releases via GitHub

The interface is pretty simple:

  1. Pick a commit or Tag to use as the release source.
  2. Write-up the Release title and notes.
  3. Add any artifacts that the source code archives don't already contain.

The challenge is with embedding version information into the application. For example, while developing new features, so long as they are compatible with previous releases, the Minor version should be incremented. Also, for bugfixes for the Minor version should increment the third number using the semantic versioning system. If the versioning is embedded into the code, then as dev branches are merged-in to the staging branch prior to release, the versioning information will get overwritten. If a particular Minor or Bugfix version increment does not make it to Staging, then the numbering system leading up to 'latest' will appear to skip numbers, and the correlated commits to Staging won't explain why the versioning is not orderly.

If I relax my view of how semantic versioning works, this really isn't a problem. But I have to ask the question how to work through (or around) this so the numbering system will work during pre-release testing and demos, acceptance testing once changes are staged, and for final versioning before official release. I'm certain there are tools and techniques to get this to work more easily

Week 26 and 27

Bigfoot Bib Report Form v2.0

After a few days of juggling more ideas on how to handle users' input of time in 24-hour format, I settled on a set of functions that carefully identify and process the hours and minutes bsaed on whether or not a colon is present.

It is very difficult to anticipate and cover every possible input from a user, so I made some assumptions about common inputs and mistakes (based on my own experience) and will convey the expected behaviors to the end users.

After a couple more surprise bug fixes, I've decided to release v2.1.4. A demonstration will take place during a Monday night Zoom session with the team lead and other Bigfoot volunteers.

Mobile Weather App and API Changes

NOAA and the NWS updated the weather API, which broke my latest Mob-WX updates. I have a work item on my backlog to fix the issues.

Thinking further out, it would be a good idea to develop and deploy an API Gateway so that the mobile app doesn't have to break and get revisioned and instead, silent updates can happen at the API Gateway that will support several minor version releases of the mobile app itself.

Plenty more work will be necessary to make that happen and I anticipate it will be fun and interesting.

VSCode Extension Updates: Create-TOC

It has bee 1 year since I released my first VSCode Extension and it is in need of several updates and promised feature delivery:

Last weekend I started working on addressing the above issues, as well as preparing to update Github Actions to enable build and publish capabilities.

Overall: Success! There is more work to do to ensure that pre-release publish only happens at a particular action. For now I've set it to a particular branch. A better change (later) would be to only publish on a particular tag, which I'll figure out some other time.

Week 25

MobWx BugFix Forecasts

Worked on my Mobile Weather App, fixing bugs. There are some architectural issues (I'm now realizing) that will need to be addressed over time. For right now though, it should be fine. Some takeaways:

Extension Methods - Oh My

I also read about Extension Methods in C# (F# and Visual Basic too) and made some notes about extension methods. Some key takeaways:

BF-BMX Full Setup Test

Visited Rob and Phil to work through setup, deployment, and usage of BF-BMX Desktop and Server components across multiple computers, WiFi networked, with Winlink Express for sending/receiving messages with bib data.

Some key takeaways:

To combat the last issues in the last bullet point, I make some changes:

  1. Updated the activity log entries to be more clear about what the software is doing, how long to wait for a server response, and whether there was a response, and if so whether it was success or failure.
  2. If the POST request failed the log would write a Warning instead of an informational message.
  3. When the POST request succeeded, the log file text states a confirmation.
  4. Each log entry now includes the specific message ID that it is refering to.
  5. The Timeout has been configured down to 20 seconds (15 seconds for DNS timeout + 5 seconds for request/response events to complete.

I decided that a Failure activity log entry was not necessary for failed attempts to send data to the Server because a legitimate scenario is to run the system without a server in the mix at all. In the future I may revisit this and avoid logging these errors when it is known a server-side will not be included.

A new RC will be posted to the BF-BMX project site which, to the best of my estimating, will probably be the final version before this year's event. :boom: :tada: :beers:

Week 24

I've been busy on several fronts. In software development I continued updating the Bigfoot Bib Report Form, and also started updating my Mobile Weather App. When I realized I'd not done any code challenges for a few days I did some deep diving into Trees. Rod Stephens' book [Essential Algorithms] has been useful and this time I drove right-on through the entire Trees chapter, implementing pseudo-code into real code, and completing challenge questions along the way.

Some key takeaways from the last 5 days:

Basic Binary Tree Recursive Inorder Traversal

While working through the Trees chapter I was using JavaScript, however the code was essentially the same as this C#:

public class MyTreeNode
{
  public int Data {get;set;}
  public MyTreeNode? Left {get;set;}
  public MyTreeNode? Right {get;set;}
  private List<int> Visited {get;set;} = new();

  public List<int> getNodesInorder() {
    this.Visited = new List<int>();
    traverseInorder(this);
    return Visited; // let the caller deal with the result values
  }

  public void traverseInorder(MyTreeNode currentNode)
  {
    if (currentNode.Left != null) 
    {
      traverseInorder(currentNode.Left);
    }
    // process currentNode, here it is added to a Visited list
    this.Visited.Add(currentNode.Data);

    if (currentNode.Right != null)
    {
      traverseInorder(currentNode.Right);
    }
  }
}

It's also possible to use a while() looping structure to do this, and there are pros and cons to each:

Sorting Challenge Question

Of course I'm off-track, having been distracted by an interview question: "Which sorting datastructure uses no additional storage?"

First of all, I need to continue training my mind to ask sorting-algorithm questions (to myself or otherwise) so that I can hone-in on a reasonable solution:

Looking at Big-O Algorithm Complexity Cheat Sheet here are some possible answers to the original challenge question:

In line with this thinking, I completed developing Heapsort in JavaScript following guidance in Rod Stephens' book [Essential Algorithms], and updated my repo with the code. The README walks through the Heapsort algorithm.

Week 23

Battling JS and Browser Compatibility

I've been working on updating a Form used in the HAM community to track event participants, such as a marathon runners. The form is designed as a single-page web form with HTML, CSS, and JavaScript for layout, style, and functionality. In the original form, there is some focus on maintaining compatibility back to the Windows 7-era (about 2011).

Using MDN and CanIUse.com to determine what JavaScript built-ins would be safe to use because very difficult and tedious. I used a spreadsheet to help track what I'd already looked up and to record compatibility levels of built-in methods, statements, and expressions.

I discovered a better way: Why not just stick with the methods that are already in use by the form, and avoid adding newer methods until there is a clear signal from users that upgrades to a newer era (Windows 8.0) can be implemented?

  1. Locate existing JavaScript functions, expressions, statements and make note of them as "good to use".
  2. When creating new functionality or customizing new or existing methods, refer to this to maintain compatibility levels.
  3. Whenever newer built-ins, statements or expressions are wanted, note them on the spreadsheet for future implementation.

This allows for deferring research until later. Instead, existing compatible techniques can be implemented right away, keeping momentum moving forward.

MAUI Project GitHub Actions

Managed to get .NET MAUI 8 building with artifact generation in GitHub Actions. Some key takeaways:

Building MAUI in .NET 8

.NET MAUI 8 is a pretty challenging framework to work with. I love the results, having a Windows Desktop App that is (very basically) also an Android App - great stuff! In hindsight, I should have looked into Xamarin several years ago when I got going with .NET.

Recent activity has been to start-up a second sprint to update and build-out my mobile weather app, and prepare it for deployment in the Google ecosystem. There are many hurdles to overcome, but I've knocked out a couple so far:

Single Page Web and Accessibility

Spent a good amount of time debugging, adding-on to, and prepping the Bigfoot Bib Report WL Form project for the next big version. It's not clear whether it will be pressed into service - I'll have to get a few friend hams involved to take a look at the form, find issues, and provide feedback.

Some things I learned along the way:

Week 22

After 3 days of focusing on learning modules and new concepts, I took a break and worked on some field operations planning and setup involving a Raspberry Pi Zero2 W. Working with Linux is getting easier, and the biggest issue has been finding consistent documentation. Not all docs are created for the version of RaspberryPi OS that I'm working on, so some packages either aren't available, or don't work (properly or the same). Also, some documents leave a lot to be desired, for example a manpages on debian.org had a lot of "should-be" and "probably" remarks in it, which doesn't sound all that promising.

Going forward, for certain projects I'm going to stick with Bullseye 32-bit for any legacy or micro RPi projects for now, including the RPi 4. If I get my hands on an RPi 5 and/or Bullseye approaches EOL for those legacy RPis, I'll start moving over to Bookworm.

Completed A Build 2024 Challenge

On Saturday I completed all 19 modules of "Accelerate Developer Productivity with GitHub and Azure for Developers"! This took a big effort, and I took (way too many) notes. Thanksfully some portions covered topics I already has experience with, and the other areas were great fill-in to help me build out my skill sets and build up experience.

Polyglot Notebook Learnings

I took some time out to learn more about and make notes about Polyglot Notebooks. I have much more to learn but these seem like a good tool.

Tree Review

It's been quite a while since I've reviewed Tree data structures.

Terminology:

Types of Nodes (and therefore, trees):

Some Basical Calculations:

BigO Analyses (N = Nodes):

Coding Trees:

public class BinaryNode
{
  public int Data;
  public BinaryNode? LeftChild;
  public BinaryNode? RightChild;
  public BinaryNode(string data)
  {
    Data = data;
  }
}
// instantiate Nodes
BinaryNode root = new(4);
BinaryNode node1 = new(1);
BinaryNode node2 = new (10);
// etc

// build a Binary Tree
root.LeftChild = nod1;
root.RightChild = node2;
// etc

And for an N-Degree Tree:

public class TreeNode
{
  public int Data;
  public TreeNode[] children;
  public TreeNode(string data)
  {
    Data = data;
  }
}

Note: It is possible to add a Parent reference to BinaryNode or TreeNode so that it is easier to traverse 'up' the Tree.

Information about Branches can be stored if necessary, but this topic is more relevant for Graphs and Networks.

Traversing A Tree:

Preorder Traversal:

public class TreeNode
{
  // Fields and CTOR
  public void TraversePreorder(BinaryNode currentNode)
  {
    // process node here e.g. push Data into an array, output to console, etc.

    if (currentNode.LeftChild is not null)
    {
      TraversePreorder(currentNode.LeftChild);
    }
    if (currentNode.RightChild is not null)
    {
      TraversePreorder(currentNode.RightChild);
    }
  }
}

Note: It is possible to add a helper Class that will do this for a Node. This allows a single object to store results from the method, such as an Array of traverse Node values, rather than storing the structure within each Node.

Note: This traversal can be used to traverse N-degree Tree Nodes with N greater than 2.

Inorder Traversal:

public class TreeNode
{
  // Fields and CTOR
  public void TraverseInorder(BinaryNode currentNode)
  {
    if (currentNode.LeftChild is not null)
    {
      TraverseInorder(currentNode.LeftChild);
    }
    // process this node here
    
    if (currentNode.RightChild is not null)
    {
      TraverseInorder(currentNode.LeftChild);
    }
  }
}

Note: This is effective for Binary Tree Nodes, but is ambiguous for Tree Nodes with more than 2 Child Nodes.

Postorder Traversal:

public class TreeNode
{
  // Fields and CTOR
  public void TraversePostorder(TreeNode currentNode)
  {
    if (currentNode.LeftChild is not null)
    {
      TraversePostorder(currentNode.LeftChild);
    }
    if (currentNode.RightChild is not null)
    {
      TraversePostorder(currentNode.RightChild);
    }

    // process this Node here
  }
}

Note: This traversal can be used to traverse N-degree Tree Nodes with N greater than 2.

Breadth First Traversal

public static class BinaryTree
{
  // Fields
  public Queue<BinaryNode> LevelNodes = new();
  public void TraverseBreadth(BinaryNode currentNode)
  {
    LevelNodes.Enqueue(rootNode);
    while (LevelNodes.IsEmpty == false)
    {
      BinaryNode currentNode = LevelNodes.Dequeue();
      // process the current node e.g. output its Data or store Data in a string or array, etc.

      // add children (the next Level) to the Queue
      if (currentNode.LeftChild is not null)
      {
        LevelNodes.Enqueue(currentNode.LeftChild);
      }
      if (currentNode.RightChild is not null)
      {
        LevelNodes.Enqueue(currentNode.RightChild);
      }
    }
  }
}

Leveraging FIFO ordering guarantees all Nodes at a particular Level are processed before moving to the next Level.

BigO Analyses:

That's enough for now. It's always good to review these concepts. One day, it will be much easier for me to grasp and use them.

Week 21

MS Build 2024 is happening this week and will consume a large chunk of time. I have a schedule set, and am looking forward to learning all that MSFT and their partners have to share!

Build 2024 Highlights

There were many other interesting takeaways, many more details I want to explore, and several MSFT Learn Modules I want to work through now. That is a sign of a successful event!

Semantic Structural Elements

I've learned that Semantic Elements are helpful when developing accessible, screen-reader-ready websites. While going through Microsoft Learn Blazor Modules, I've been trying to reinforce what I've learned by using. Here is a list of common Semantic Elements, sourced from Mozilla Developer Network (MDN):

The above is the list of content sectioning elements. I like to think of these as structurally significant elements that are critical to developing an accessible webpage from the start. There are many other element types that can (and should) be used as designed, here are the groups:

There are also a ton of deprecated elements that should not be used.

VSCode Run and Debug Blazor with Alternate Browser

While working through Blazor training, I kept finding myself opening Firefox or Edge and typing-in the localhost and port of the running Blazor Server, because I didn't want to be forced to use Chrome or switch the OS default browser to test a site on other browsers.

This took just a little investigating, but here is what I did:

  1. The launch.json file contains a collection of launch configurations that fills the list of options in the Run And Debug tool's F5 button.
  2. According to starting a web browser article on code.visualstudio.com, the key to launching browsers lies in serverReadyAction and launchBrowser, the former being a new feature, the latter being an older (but still supported) feature.
  3. By default, when executing "NET: Generate Assets for Build and Debug" in the Command Palette, serverReadyAction is added and configured, which calls the OS-default web browser, or the VS Code-configured default browser, if edited. That's fine, but if I want to have a selection of browsers to launch, I need to have multiple configurations to choose from.
  4. Configuring a new 'configurations' collection item was not difficult. I pretty much copied the entries from the ".NET Core Launch (web)" entry, and edited it for launching Firefox.
  5. The last bit was to add the path to Firefox.exe to the "windows.command" properties in the new configuration.
  6. There are 2 additional commands, "osx.command" and "linux.command" that should probably be added for those instances where iOS or Linux-based development is necessary. In this case they are not necessary since I'll be sticking with Windows, so I removed them.

Here's a sample showing only the configuration item that launches Firefox (other configuration items were omitted):

{
  "version": "0.3.0",
  "configurations": [
    {
      "name": "Launch In Firefox (web)",
      "type": "coreclr",
      "request": "launch",
      "preLaunchTask": "build",
      "program": "${workspaceFolder}/bin/Debug/net6.0/BlazingPizza.dll",
      "args": [],
      "cwd": "${workspaceFolder}",
      "stopAtEntry": false,
      "launchBrowser": {
        "enabled": true,
        "args": "${auto-detect-url}",
        "windows": {
          "command": "${env:ProgramFiles}\\Mozilla Firefox\\firefox.exe"
        }
      }
    },
    {
      "name": ".NET Core Attach",
      "type": "coreclr",
      "request": "attach"
    }
  ]
}

Be certain to verify the bin/Debug dotnet version in the path and the dll filename are correct.

Blazor In The Past

When I was building my .NET MAUI application "Mob-WX", I built a Blazor Server that could accept APK files and serve them up for rapid deployment to my physical Android phone. The server uses an MS-SQL back-end to map files on the file system to user-friendly names and dates, and allow adding and removing entries and files locally.

Every now and then, the SignalR connectino would break between the Browser and the Blazor Server, and I didn't understand why. After completing some Blazer Server training modules, I've learned that a Blazor Lifecycle Method code block is probably throwing an unhandled Exception, and breaking the SignalR connection is the default behavior after such an event.

I'll have to go back to that project and add appropriate Exception Handling. Hooray for continual learning and self improvement!

Update: I've completed the planned Blazor learning modules! :tada: On to the next thing!

Challenges Ahead With Continued Learning

I've registered for the MS Learn Challenge - Build 2024 Edition and have a plan to get this Plan's Modules knocked out by Tuesday end of day.

Week 20

OSS and TS

Coding and transpiling TS is an interesting adventure, especially when looking at a project other than my own. Seems like there are issues with walking dependecy trees, either by the IDE and/or currently installed Extensions, so there are lots of red squigglies on screen. This is very distracting and I've asked around for help but haven't received any responses so far. I'll push forward anyways.

Blazor Server and Hybrid

After reviewing my progress on MSFT Learn modules after several weeks away from them, I discovered some ASP.NET and Blazor modules I had started but not yet completed. Upon completing those I started looking at Blazor as a framework that could help build several projects going forward:

DSA Reboot

I've restarted practicing DS&A challenges. In the last few weeks I've lost a bit of familiarity in this area due to focusing on other projects.

Quick review of a Singly Linked List with Insert and GetValueAfter methods:

public class LLNode
{
  public int? Data {get;set;}
  public LLNode? Next {get;set;}
}
public class SinglyLinkedList
{
  public LLNode? Head {get; private set;} = null;
  public bool IsEmpty => return Head is null;
  public SinglyLinkedList(int data)
  {
    Head = new LLNode(data);
  }
  public void Insert(int data)
  {
    if (IsEmpty)
    {
      Head = new LLNode(data);
    }
    else
    {
      LLNode newNode = new(data);
      newNode.Next = Head;
      Head = newNode;
    }
  }
  public int GetValueAfter(int preceedingData)
  {
    LLNode? current = Head;
    while (current is not null)
    {
      if (current.Data == preceedingData &&
          current.Next is not null)
        {
          return current.Next.Data;
        }
      current = current.Next;
    }
    // If an existing Exception type does not already exist, create one that inherits from Exception
    Exception NotFoundException = new("Could not find value in this list.");
    throw NotFoundException;
  }
}

Quick review of a Stack datastructure:

public class MyStackNode
{
  public int Data { get; set; }
  public MyStackNode? Next { get; set; }
  public MyStackNode(int data)
  {
    Data = data;
    Next = null;
  }
}
public class MyStack
{
  public MyStackNode? Top { get; set;} // null means empty
  public bool IsEmpty => Top is null;
  public void Push(int data)
  {
    if (IsEmpty)
    {
      Top = new MyStackNode(data);
    }
    else
    {
      MyStackNode newNode = new(data);
      newNode.Next = Top;
      Top = newNode;
    }
  }
  public int Peek()
  {
    if (IsEmpty)
    {
      Exception EmptyStackException = new("This stack is Empty.");
      throw EmptyStackException;
    }
    return Top.Data;
  }
  public int Pop()
  {
        if (IsEmpty)
    {
      Exception EmptyStackException = new("This stack is Empty.");
      throw EmptyStackException;
    }
    MyStackNode temp = Top; // possible null value here
    int topData = temp.Data
    Top = Top.Next; // possible null value here
    temp.Next = null;
    return topData;
  }
}

Bugs Bugz Boogz

Sometimes there are surprising features in WPF. For example, implementing Binding Validators on Controls can have the side-effect of the Source property not receiving the data that did not pass validation. I'm sure this is by design and, with a little thought, it can make sense. After a few hours of tracking down a pesky bug in BF-BMX Beta 2, I concluded that the custom validation would not be compatible with updating the on-screen buttons and on-screen status updates. I'll need to look into an alternative means of providing on-screen feedback to the user when they've entered an invalid path.

Another bug that I invented while architecting the file system monitor wrappers is ignoring the difference between the nullable wrapper class, and the nullable FileSystemWatcher class itself:

Going forward, I'll have to refactor the code to either:

  1. Refactor the null-checks to differentiate between the FileSystemWatcher instance being null, vs the wrapper class (and it's encapsulated instance) being null, or
  2. Bubble-up the wrapper-class properties to become Observable to the ViewModel.

Week 19

BF-BMX Beta 2

At the May 1st meeting, discussion around tweaks and alterations led to a few new features. Implementing them was not too difficult and new builds were produced on 7-May for evaluation.

Some takeaways from this feature-update and debugging work:

Web Accessibility Learnings

Completed MS Learn Web Accessibility Basics. It was focused on ASP.NET webapp but concepts can be applied to any website and some aspects can be applied to Windows App, too.

Key takeaways:

Week 17 and 18

Of Course There Are Bugs

When aren't there?

Working though tweaks discovered during the BF-BMX Beta Launch meeting, as well as bugs found since then, I'm confident to say that the bugs will never end. The question is: Can I address the bugs correctly to minimize the impact of remaining bugs, known and unknown, going forward?

Meanwhile, some key takeaways:

Release Build and Publish Activities

Lessons learned while developing Published App configurations:

Azure Developers DotNET Day 2024

I attended this very rapid-paced, multi-topical stream of sessions revolving around developing on top of Azure services.

Here are my hastily written notes.

While I had some down time, I took a look at dealing with a few issues with the BF Race Tracker form.

VS Code Day 2024 Notes

Some succinct takeaways from the 24-April-2024 event I attended (and enjoyed):

Week 15 and 16

Throughout this week I've been focused on the BF-BMX project. I'll be meeting soon with at least 1 of the key end users soon to go over current status, find out what needs to be done, and to prioritize that work accordingly. FOr the last several weeks, as I've implemented features and squashed bugs, I've been focused on maintaining a working product between PRs. This has made it possible to work "ahead" of some scheduled work items, yet still be able to "go back" to a previous branch, make progress and/or fix bugs, and still be able to deploy a Debug or Release build for hands-on testing at pretty much any time.

It's surprising how much a project can change, even without specific design instructions to do so. For example, I have built out a custom Linked List to deal with a need for a FIFO-like queue operation with custom features. A standard Queue would not necessarily meet this need. After additional research, it turned out the custom data structure wasn't necessary, so it was removed from the project. This has happened a few times. At least I learn a little each time it happens:

There are a good number of concerns about how to properly parse plain text, especially if it is delimited in multiple ways (i.e. tabbed, comma, and/or spaces). While tab- and comma-delimited are not too difficult to deal with, I explored enabling space-delimited parsing and it became complex very rapidly. If space-delimited parsing is necessary, it will probably end up being a 2- or 3-stage process to ensure random sections of unimportant/unexpected data are not captured as "possibly good data".

Using Moq

I've come to realize that Mocking components of BF-BMX is necessary in order to perform unit testing. It has also become apparent that file access is unavoidable, given the requirements definitions for this solution. So off to reasearch Moq and start trying to use it! Here are some key takeaways:

This Code Magazine article: Using Moq A Simple Guide To Mocking for .NET was helpful.

Other Ways To Fake Stuff

Since BFBMX is based on incoming data that is relational in nature, and Entity Framework was already added to the core system for future use, I attended an online discussion about Bogus, a faker.js spin-off Package for .NET.

In the discussion and demos were some key takeaways, and I feel like Bogus is probably a package I should explore for BFBMX or other project going forward:

Open Source Follies

I took a look at some open source projects that looked interesting to learn, use, and possibly contribute to. A common (and unfortunate) theme a lack of directing members to lead core project activities like managing pipelines/CI-CD, and maintaining release cycles and general project management. On occasion, the situation is related to a parent-project that is going to increment to a new major version, and the child project won't get any updates until after that increment happens to the parent. Another common theme is Issues that are closed (or effectively closed) but still marked as "Needs Help" (or similar), but have not been updated in more than 1 year.

Any or all of these situations make it more difficult to get excited about actually using and becoming a contributing member of the community.

I will plan to revisit Humanizer in a few months, and meanwhile keep my eyes open for other interesting opportunities.

As for my personal OSS projects, it just so happened I needed to set up a Linux environment to work on a second project of mine. This forced me to install and configure WSL on my Surface Pro, and install the latest NVM so I could install the latest Node and NPM, and run the project's Express.js server.

Here are some highlights:

Some more personal OSS experience: I went to explore refactoring some HTML, JS, and CSS website code for a specific purpose. Within 40 minutes I had a (very) simple website up and running with the intended feature functioning. It took a little longer to tweak the feature and determine just how much farther the feature could go (without becoming a lot of work), but this resulted in a go-forward plan and I am excited to see how it comes out.

Regex Interpretation of Alternate Meta Sequence

One requirement I had was to match a string of characters that included either a tab, or a comma with an optional following space.

Example cases: 123, ABC or 123,ABC or 123\tABC.

I came up with a Regex Pattern of \b\d{1-3}(,\s?|\t)\w{1-3}\b but that would not properly capture all three cases, and it was difficult to understand why not.

After 15 minutes of fiddling with the pattern I asked GitHub Copilot how to build a Regex pattern that would meet a need like "1 to 3 digits followed by either a comma with or without a single space, or a tab, followed by 1 to 3 word characters". GHCP came up with the same pattern and explained (incorrectly, it turns out) how it worked.

So I spent another 20-30 minutes using regex101.com to work out what the problem was, and how to create the correct pattern. Microsoft's Learn documentation on dotnet standard regular expressions has a link to a PDF Cheat Sheet (that I had forgotten about! :wow:) that also came in handy.

Turns out the problem was how the pattern was actually being interpreted, based on how the Alternate Meta Character was being applied |.

In order to avoid the pattern from evaluating as:

"1 to 3 digits followed by a comma and either an optional space or a tab..."

The incorrect evaluation was corrected by applying the Non-Capture Group Construct (?:...) to surround the alternate comma or tab argument, and to place the tab character before the alternate character like so:

\b\d{1-3}(?:\t|,\s?)\w{1-3}\b

Lessons learned:

Week 13 and 14

Sorted Dictionary and Finding Missing Data

I recently completed a LeetCode exercise where the input was an array of signed integers, and the goal was to return the smallest integer that was not in the array. For example, the solution should process an array input of [ 1, 3, 5, 4, -1 ] and return the integer 2. Additional constraints were included such as O(n) Runtime and O(n) or better storage.

I used the DotNet class SortedDictionary<TKey, TValue> as a simple and fast storage mechanism. Sorting is helpful when looking for specific values, but writing the correct, efficient sorting algorithm is usually challenging and time consuming. By storing the input data to a SortedDictionary, using 'Value' as the 'Key' and the current value index as the 'TValue' value, it is possible to find missing indices. Since the goal is to find the lowest value missing from the input, it is fairly simple to compare the input indices to the stored KVPs in the SortedDictionary, and as soon as an index is not found, return that index and that is the value that was missing from the input.

// Basic SortedDictionary<K,V> usage for this challenge
int[] inputArr = { 1, 3, 5, 4, -1 };
SortedDictionary<int, int> sortedInput = new();
for(int idx=0; idx < inputArr.Length; idx++)
{
  int currentValue = inputArr[idx];
  // skip any values that are 0 or less, or greater than the length of the inputArr
  if (currentValue < 1 || currentValue > inputArr.Length)
  {
    // skip to the next iteration to save storage space
    continue;
  }
  // SortedDictionary will throw an Exception if you try to add a KVP that already exists
  if (sortedInput.ContainsKey(currentValue) == false)
  {
    // Add the VALUE of the input as the KEY 
    sortedInput.Add(currentValue, idx);
  }
}
// more code...

Once the SortedDictionary has all of the values greater than 0 but less than the length of the input array, use the SortedDictionary as a lookup table. Start at index 1 (per constraint) to return a value or null. If null, return that value, otherwise iterate to the next value (SortedDictionary TKey) until one is missing. If all values in the SortedDictionary are contiguous, then the return value is one greater than the count of items in the SortedDictionary.

Example depiction: Find missing value using SortedDict

I've purposefully avoided giving too much detail above, other than to demonstrate one possible usage of SortedDictionary<TKey, TValue> to solve one of many code problems. My solution was not very performant in run time or storage, and it should not be referenced as a basis to solve a similar sounding challenge. Readers are responsible for following code challenge rules which could include not using a resource like this to assist them directly.

MSFT Reactor GitHub Certifications

Attended a MSFT Reactor session about GitHub and its Certifications.

From Desktop To Server Side BF-BMX Work

Now that the Desktop component is about 75% functional, it was time to start integration testing to see how Desktop and Server components are working together. They weren't so some debugging was necessary to fix them. Now they are talking to each other and there are fewer exceptions being thrown, however the API Server isn't logging anything to file other than the Message data and Bib records, so that is the next logical step before continuing integration testing. Having file based logs will help with troubleshooting and verifying functionality from here on out!

In the future I'll need to re-write the logging mechanisms to be portable, rather than tied so closely to the Desktop and API Server projects. For now it is good enough, and having a refactoring exercise to perform in the future won't impact the initial release much (if at all).

The ViewModel code is getting a bit lengthy and difficult to read. This tells me I need to encapsulate some of the state and functionality. Doing so will have to wait until a few more features are completed: A meeting with the stakeholders will be necessary (soon) to ensure the outputs and functionality are going to meet expectations, and to tweak (or reset) expectations that have changed or were otherwise not well understood.

Multi-directory monitoring is functioning in debug sessions, and in systems-test scenarios using actual Winlink Express and running SUT Release Builds.

A presentation has been put together that overviews the system main components and features, introduces how to configure the desktop and server, and discusses the operation and logging aspects. In a future meeting (soon, tbd), the presentation and a demo will be performed, which should help coax inputs on necessary changes and tweaks, prior to the scheduled May 1st Beta release.

Microsoft JDConf 2024

Interesting online conference about Java, JVM, and support for Java App development in VS Code, and running Java Apps in Azure!

This is exciting news for the Java community, and for me the onramp to building Java Apps in VS Code is flattened through simplification of Java project setup and other aspects of the software development lifecycle.

Week 11 and 12

Completed some interview preparatory work, including a LeetCode challenge to convert from Roman Numerals to Integers using JavaScript. I've solved a similar problem some time ago using Java, but it took me about 2.5 hrs to diagram, pseudocode, step-through, code, and evaluate its performance.

Some key takeaways solving LeetCode Roman to Integer:

While I was at LeetCode, I took a look at one of my previous submissions and noticed the BigO in Time was very poorly ranked. It took me about 15 minutes to refactor the code to get a better execution time that was closer to 50% of all ranked submissions. Storage space was also average, but the spread of space utilization was so small that it really doesn't matter (i.e. 50MB vs 51 MB is just a rounding error for a C# compiled application size).

After lunch I decided to do another LeetCode challenge. This one was to return the most common prefix characters from an array of strings:

At the end of the week, I sorted out some known issues with BF-BMX and am getting ready to implement additional "Watchers" in the app:

Lastly, I put in some extra effort to prepare for interviewing. I'm tweaking my schedule to get these tasks to be more regular. There have been a few very interesting open positions posted recently that I look forward to researching to learn more and possibly apply for.

ILogger and Custom Logging

Writing log information is an important feature of an App. During development and debugging, it can provide an audit trail of operations happening under the hood so that issues can be traced to the source more easily. When an App is released, an end user can review the logs to help confirm the App is "doing the right thing" or as breadcrumbs to determine the cause of unexpected behavior. In the past I've developed a couple different logging services that were crude and simplistic (they worked fine for very low activity apps), or utilized .NET built-in ILogger functionality to get Console-level logging output. For BF-BMX, it was important to get a more robust and scalable file-logging solution in place for the desktop application.

I took extra time to learn and understand how to create a custom logger in .NET, and here are a few outcomes from getting it going for the first time:

This was a difficult thing to implement because:

Where I had to trust .NET to do some work for me once I've set up the classes per the interface requirements:

An issue that I knew would come about was Logging from multiple parallel tasks could cause IO Exceptions while attempting to write logging output. At least logging is implemented at a basic level and I can work around parallel IO by redesigning the logging a little bit.

Dependency Injection and the Factory Pattern

Watched Factory Pattern with Dependency Injection by Tim Corey regarding the Factory Pattern. Some key takeaways:

Factory Method Pattern

About Design Patterns

Tim Corey mentioned the following in Factory Pattern with Dependency Injection:

Debugging Blazor and Other Async Systems

I ran into an issue where a Blazor app was calling JavaScript (through .Net interop classes), and the JS code would call .Net back to update a field in the razor file, but the change would not show on the page. JS was delayed in returning a response, so some asynchronous processes were at play.

Key takeaways:

Note: Blazor StateHasChanged(): Notifies Blazor component that bound properties have been updated.

Recent LeetCode Practice Session Takeaways for March 2024

I should start finding ways to make these challenges more fun to complete, rather than over-challenging myself by not preparing for them in any way. For example: When I first see a Linked List challenge that I want to work on, I should:

  1. Review how a Linked List can be traversed.
  2. Review the difference between while code blocks and recursive methods.
  3. Brain dump the properties and methods of a LinkedList and its Node type.

Week 9 and 10

Completed initial BF-BMX API Server build. All updates are documented in the README. There are some open questions about the output logging formats. During implementation, I knew changes to logging might be necessary so I've made it relatively easy to change the logging while minimizing how much code is touched or affected.

Implemented many tests against the BF-BMX service and API, and started running some simple input-output testing using the Swagger UI.

The BF-BMX user interface is the next step. Leveraging .NET 6, WPF, and the Community Toolkit, my goal is to focus on the functionality of the UI. There are several synchronous and asynchronous processes running under the hood, and these need to work in order for this project output to become useful. Once the functionality has been well tested, style and UI tweaks will be added for an attractive, useable interface.

Attended MS Reactor session about dev productivity, dev flow and artificial intelligence, and other resources and tools to help with developer productivity.

The Developer "Inner Loop"

Azure Developers JavaScript Day Notes

Collection of random thoughts taken while attending Azure Developers JavaScript Day hosted by Microsoft and Microsoft Azure Developers.

What GitHub Copoilot Can Do:

What is Retrieval Augmented Generation (RAG)? It is a code pattern used to leverage augmented capabilities of LLMs.

What is LangChain/LangChainJS? Framework for developing Apps using backend LLMs.

How can Copilot be configured to query my custom data?

Related References

Playwright

Max and Stephan ran a great overview of Playwright!

JS Cloud Skill Challenge

Week 8

Community Toolkit MVVM

Began reading up on DotNET Foundation project CommunityToolkit MVVM. I'm a little worried about this project but initial impressions are it is a handy code-generator for things like object observability, notification, commanding, and messaging in WPF (and UWP, Xamarin, and possibly others).

I'll do some experimentation before I decide whether to utilize the CommunityToolkit for BF-BMX.

WPF - An Observable Queue?

I had a silly question, wondering if a WPF control could display a Queue of items. To further complicate the question, the queue would be accessed asynchronously by another process to enqueue and dequeue items.

I came up with a Synchronous solution that involves inheriting from List<T> and overriding InsertItem() and RemoveItem(), and also adding Enqueue(Object) and Dequeue() methods for code readability.

First, set up EventArgs for the custom queue:

public class PersonChangedEventArgs : EventArgs
{
  public readonly Person ChangedItem;
  public readonly ChangeType ChangeType;
  public readonly Person? ReplacedWith;

  public PersonChangedEventArgs(ChangeType change, Person item, Person? replacement)
  {
    ChangedItem = item;
    ChangeType = change;
    ReplacedWith = replacement;
  }
}

public enum ChangeType
{
  Added,
  Removed,
  Replaced,
  Cleared
};

Next, inherit from ObservableCollection<T> and insert EventHandlers:

public partial class ObservableQueue : ObservableCollection<Person>
{
  public event EventHandler<PersonChangedEventArgs>? Changed;
  public List<Person> People { get; } = new List<Person>();

  // add an instance to the end of the List
  public void Enqueue(Person person)
  {
    People.Add(person);
    base.InsertItem(Count, person);
  }

  protected override void InsertItem(int index, Person newItem)
  {
    base.InsertItem(index, newItem);
    EventHandler<PersonChangedEventArgs>? temp = Changed;
    if (temp != null)
    {
        temp(this, new PersonChangedEventArgs(ChangeType.Added, newItem, null));
    }
  }

  // remove the first item (lowest indexed) from the List
  public void Dequeue()
  {
    RemoveItem(0);
  }

  protected override void RemoveItem(int index)
  {
    Person removedItem = Items[index];
    base.RemoveItem(index);
    EventHandler<PersonChangedEventArgs>? temp = Changed;
    if (temp != null)
    {
      temp(this, new PersonChangedEventArgs(ChangeType.Removed, removedItem, null));
    }
  }
}

Then, in the ViewModel, implement the code-generating Attributes:

public partial class MainWindowViewModel : ObservableValidator
{
  [ObservableProperty]
  private ObservableQueue people = new();
  // other observable property fields here like FirstName, LastName, etc
  [ObservableProperty]
  private string addPersonButtonText = "Add Person To Database";
  [ObservableProperty]
  private string removePersonButtonText = "Remove Person From Database";
  public string FullName => $"{FirstName} {LastName}";
  [RelayCommand(CanExecute = nameof(CanSetName))]
  public void AddPerson()
  {
    // instantiate newPerson and other processing, logging, etc code here
    People.Enqueue(newPerson); // add to end of the list (highest index)
    PeopleCount++;
    OnPropertyChanged(nameof(PeopleCount)); // notify change in count
    // null-out newPerson and FirstName and LastName fields
  }

  public bool CanSetName()
  {
    // if FirstName and LastName have text in them...
    AddPersonButtonText = $"Add {Fullname} To Database";
    OnPropertyChanged(nameof(AddPersonButtonText));
    return true;
    // else, log this situation...etc
    return false;
  }

  [RelayCommand(CanExecute = nameof(CanRemovePerson))]
  public void RemovePerson()
  {
    // other processing, logging, etc code here
    People.Dequeue(); // first item in the list
    PeopleCount--;
    OnPropertyChanged(nameof(PeopleCount));
  }

  public bool CanRemovePerson()
  {
    RemovePersonButtonText = $"Remove Person from DB ({PeopleCount})";
    OnPropertyChanged(nameof(RemovePersonButtonText)); // notify of button text change
    // if additional processing is necessary, expand
    // the return statement to a full if-then code block
    return PeopleCount > 0 ;
  }
}

Implementing asynchronous operations would be the next step, and enabling concurrent access to the List will be another hurdle. If the above code is used, added code to enable async and thread safe concurrency will be posted here.

End of Week 8 Comments

The last few days I have been working on implementing code and tests for the BF-BMX project. I realized there was room for improvement in defining some data details so I'm reaching out to the primary end user to get their preference on what the data should look like. This shouldn't block my progress at all, but might require some refactoring later, depending on what the response is.

There will be several interruptions in the upcoming weeks that will slow project progress, so this next week will be a big push week to get the core of the BF-BMX project ready for building-out and testing functionality. I have time to get this done before Beta testing begins, but I want to stay ahead of the schedule as much as is practical.

Week 7

Made some good progress the last few days with WPF Input Validation, implementing async functionality, and backup/restore of in-memory data (which was largely completed in week 6).

WPF Input Validation

I'll overview Tosker's Corner demonstrations of using input validation in the next four subsections.

Also check out this response by StackOverflow user MrB for more.

Remember: Updates to properties must include notifications, for example IObservableCollection, or INotifyPropertyChanged, etc implementations.

ToskersCorner introduces four ways to accomplish validating input in WPF:

A couple of these actually rely on Validation by Exception behind the scenes, so there is plenty of crossover.

See my notes in Conted: WPF MVVM Learnings.

Asynchronous Programming

This is a real rabbit hole, but it is pretty interesting albeit complex. I've written some notes in dotnet async await notes to force my brain to process what Stephen Cleary is saying in his blog post/essay.

Some key takeaways:

For BF-BMX, I will probably want to look into using AsyncCollection<T> to manage multiple processes pushing data to a common repository.

I've added notes about TAP and Aynchronous programming patterns in DotNET Aync Await Notes.

The next thing to check out is Data Structures for Parallel Programming at MSFT Learn - I have a feeling this will provide even more insight into patterns that could come in handly when developing BF-BMX.

Feb 17 DSA

The other night I had a nightmare that I couldn't depict how to zip Linked Lists on paper. I took that as a sign that I am out of practice with DSA exercises. So I took a quick side-trek to review "Big-O Notation", and will prepare for a more regular review of algorithm and data structure challenges to keep my interviewing brain fresh.

MVVM Cross

An open-source project supported by the DotNET Foundation, applies MVVM pattern to WPF, iOS, Android, and other platforms. I took a look at MVVM Cross as a possible framework to use in BF-BMX, replacing Caliburn Micro. Here are a few key takeaways:

Mobile Weather App Downloading Images

In the Interleaving section of MSFT Learn article on Task-based Asynchronous Pattern, example code shows how to utilize Task.WhenAny(func) to download images for display to a UI, as they become available. This will apply nicely to Mob-Wx on the 7-day forecasts page.

Week 6

Although I was out of town for most of week 5 some software development happened anyway:

Iterating Through Characters In A String

Method Wrapping

While building the ADIF validator toy, I found myself creating "wrapper methods" to the library methods that actually did the work.

BF BMX Kickoff

Started working on the Bigfoot Bib Message eXtractor project. My current approach to development is:

There are still some questions I need to get answers to (non-exhaustive list):

Exploring ways to get the API Server to utilize a database, item Collection, and logging. Here are a few takeaways:

Exploring file monitoring, asynchronous code, and regular expressions. Here are a few takeaways:

Entity Framework and EF Core

This 5-page feature comparison of EF Core and EF6 is probably the best TLDR: MSFT Learn: Compare EF Core and EF6. It really pushes the idea that EF Core is the way to go with new projects. That's fine, but which EF Core? Turns out there are versions of EF Core that are not supported outside of .NET Core, .NET Framework, and .NET Standard 2.0. That's also fine, but it forces designers and developers of existing products that use Entity Framework to more to EF Core (and cross their fingers) or stay with Entity Framework, which is very stable and reliable at this point. What is EF falls out of support completely, and EF Core doesn't support the features your application (or system) rely on?

Tough questions there. Thankfully, I am not going to worry (much) about using either one, outside of the immediate compatibility and feature requirements my current project needs.

Another sticky point is MSFT touts EF and EF Core as having support for so many database interfaces. While it is true there are multiple caveats and tradeoffs to consider. One example is Sqlite - It is supported, and there are EF/EF Core extensions that provide for integrating Sqlite, but Sqlite itself is less focused on being EF/EF Core compatible (and frankly, Windows-ready it seems). While Sqlite is certainly in use and a good solution for many software shops on Windows, I'm chosing to not use it for this project to avoid headaches with platform and framework compatibility and interoperability.

So, I'm going to settle on EF Core and "In Memory Database" as a simple alternative to relying on only collections, or using EF/EF Core with SQL Server or Sqlite. More likely, I'll look to building a Dapper ORM data layer, as is described by Tim Corey in his YouTube video Data Access with Dapper and SQL - Minimal API Project Part 1 where he is using ASP.NET Core Web API in .NET 6.

Old Project Unittests

I picked up where I left off with an exploratory project back in November 2023. At least 2 unittests were not working properly, and one of those was failing outright. At the time I had not worked out why the failing test was having the problem. Today I was able to sort it out:

After removing the shadowing List, validating the wrapper code functions, and replacing the indexer with a proper Get function, the Collection would behave as expected and unit tests are now passing.

This is great because the code will get folded-in to a larger exploration that will get folded-in to the BF-BMX project (if it all works out).

// one way to find a simple List item by name while inheriting from ObservableCollection<T>
public class MyCollection : ObservableCollection<MyClass>
{
  public MyClass GetItemByName(string name)
  {
    foreach (var thing in this)
    {
      if (thing.name.Equals(name))
      {
        return thing;
      }
    }

    // A caller only know about an item that exists in this 
    // collection so an error here indicates a problem elsewhere  
    // in the application logic that would need to be dealt with.
    throw new KeyNotFoundException($"{name} not found in collection.");
  }
}

Week 4

ListView and MVVM

Implementing ListView with a Template in an MVVM environment is similar to what is described below, except for where in the component tree the data becomes available, and how bindings much be changed to accommodate that change:

<!-- ForecastView.xaml code for MVVM environment, utilizing a ListView with a View Template -->
<?xml version="1.0" encoding="utf-8" ?>
<views:BaseView ...
                x:Class="MobWxUI.Views.MyView"
                xmlns:views="clr-namespace:MyProject.Views"
                xmlns:vm="clr-namespace:MyProject.ViewModels"
                x:TypeArguments="vm:MyViewModel"
                xmlns:controls="clr-namespace:MyProject.Templates">
    
    <views:BaseView.Resources>
        <ResourceDictionary>
            <controls:CustomCard x:Key="controls:CustomCard" />
        </ResourceDictionary>
    </views:BaseView.Resources>

    <ListView ItemsSource="{Binding MyCollection}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <controls:CustomCard Name="{Binding Name}"
                                         Description="{Binding Description}" >
                    </controls:CustomCard>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</views:BaseView>

Note: In my MVVM project, View and ViewModel inherit from abstract partial classes prefixed "Base". The BaseViewModel inherits from ObservableObject, and the BaseView partial class consumes a ViewModel type in the CTOR, and sets the BindingContext to the ViewModel parameter. This reduces duplicated code in every ViewModel class that is created, but makes it more difficult to realize a BindingContext does exist in each View.

The next step is styling the ListView items. Because the Bindings are now configured, theoretically all that is needed is to add BindableProperties for each Style element and then a binding reference to Resources\Styles. First attempt to configure this showed that the default binding is to the Model class (where the data comes from), so there is more investigation needed to solve this part.

MAUI ListView Control

I've been trying to understand how to leverage composition (loosely speaking) in .NET MAUI 8 to display a list of object instances within a scrollable page. In other frameworks I've been able to get this to do the work for me, including:

The high-level problem is the same, and the solution includes composing bits of UI and data to get an iterated output, which improves code reuse and limits boilerplate boringness.

Here is the high level steps to get ListView to display properly in a Content Page view:

  1. Define a data model. Ensure it has public properties with get accessors.
  2. Define a "View Template" (a ContentView, not ContentPage) that contains a Frame that binds the data model properties to Labels and other standard controls, common to each data model instance properties. Store this template in a separate folder such as "ViewTemplates".
  3. In the View Template code-behind (also a ContentView class), create public, static, readonly BindableProperty properties - one for each data model property. Avoid naming conflicts.
  4. Create a content page e.g. PageView.xaml and ensure it has <ContentPage.Resources> referencing the View Template (in this case "CardView") that will actually display the data, and also defines an x:Class that points to itself (I assume this is to ensure a reference to the collection and binding context that will be set in the next 2 steps).
  5. In the content page code-behind, define a collection that is an ObservableCollection (or inherits from it or implements an Observable interface). Ensure it is a public property with at least a get accessor.
  6. Also in the content page code-behind, set BindingContext to this.

Code samples to follow:

// DATA MODEL with get accessors
  public class Language
  {
  private string _title = string.Empty;

  public string Title
  {
    get { return _title; }
    set { _title = value; }
  }

  // ...more properties...

  // add customized colors or other styles if you really want to:
  private string _cardColor = "Azure";
  public string CardColor
  {
    get { return _cardColor; }
    set { _cardColor = value; }
  }
}
<!-- The "View Template" named "CardView" in this project -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentView ...
             x:Class="MyProject.ViewTemplates.CardView"
             x:Name="this">
    <Frame BackgroundColor="{Binding CardColor}"
           BorderColor="{Binding BorderColor}">
        <Grid RowDefinitions="Auto,Auto,Auto"
              ColumnDefinitions="*">
            <Frame BorderColor="{Binding BorderColor}"
                   Grid.Row="0">
                <Label Text="{Binding Title}"/>
            </Frame>
            <Label Text="{Binding Name}"
                   Grid.Row="1"/>
            <BoxView BackgroundColor="{Binding BorderColor}"
                     Grid.Row="2"/>
            <Label Text="{Binding Description}"
                   Grid.Row="3"/>
        </Grid>
    </Frame>
</ContentView>
// View Template Code-Behind
public static readonly BindableProperty TitleProperty =
    BindableProperty.Create(nameof(Title),
        typeof(string),
        typeof(CardView),
        string.Empty);

public string Title
{
    get => (string)GetValue(CardView.TitleProperty);
    set => SetValue(CardView.TitleProperty, value);
}

// ... more BindableProperty properties here ...

public static readonly BindableProperty CardColorProperty =
    BindableProperty.Create(nameof(CardColor),
        typeof(string),
        typeof(CardView),
        string.Empty);
public string CardColor
{
    get => (string)GetValue(CardView.CardColorProperty);
    set => SetValue(CardView.CardColorProperty, value);
}
// CTOR
public ViewTemplate()
{
  InitializeComponent();
}
<!-- Content Page "PageView.xaml" -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage ...
             x:Class="MyProject.Views.MyContentPage"
             xmlns:controls="clr-namespace:MyProject.ViewTemplates"
             xmlns:views="clr-namespace:MyProject.Views"
             Title="MyContentPage">
    <ContentPage.Resources>
        <controls:CardView x:Key="controls:CardView" />
    </ContentPage.Resources>
    <ListView ItemsSource="{Binding Languages}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <controls:CardView />
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</ContentPage>
// "View Template" code-behind
private ObservableCollection<Language> _languages = [];

public ObservableCollection<Language> Languages
{
  get { return _languages; }
  set {  _languages = value; }
}

public MyContentPage()
{
  InitializeComponent();
  // this could be a REST/JSON result object or database query result, etc
  // so long as it is an ObservableCollection<T>
  Languages = new ObservableCollection<Language>(
  [
    new Language { Name = "C#", Title = "C Sharp", Description = "The primary programming language that is used to develop apps for the Microsoft platform." },
    new Language { Name = "F#", Title = "F Sharp", Description = "Declarative-function, object-oriented, language for .NET apps." },
      // more entries...
  ]);
  this.BindingContext = this;
}

Some key ListView takeaways:

ListView Versus Content View

It seems that ListView is less-desireable to CollectionView. Performance and customizability were cited in the MAUI documentation as the reasons. I've moved the Forecast page of the Weather app over to CollectionView and it works great in Windows and in Android debug builds. Release builds are a problem though - the data did not show without jumping through a few hoops:

  1. Clear all Release and Debug builds.
  2. Remove x:DataType in the template xaml file (it was pointing to itself).
  3. Add a ResourceDictionary element with the relative path to Styles.xaml, so it would be considered in the merged resources algorithm, and Style IDs could be found.

Now the Forecast page shows data in Android Release builds, including on a physical device!

Some references:

Note: The display problem was the same in my environment, but I believe the cause was different: In my case, the compiler was probably expecting Styles.xaml to exist alongside the Template xaml, or in the View xaml.

MAUI Label, Span, and Style

There was a period where the Android Release version of MobWxApp wouldn't display the 7-day forecast data, and it wasn't apparent what the cause was. Also, since I assumed that a Release Build and Debug Build would be similar enough, testing in Debug mode would be enough. I was wrong, and here is what was going on:

So, what is the problem here?

Debug mode compiles differently than Release mode (obvious, right?). Release build doesn't provide all the feedback that Debug mode does, most notably Breakpoints and Debug log output. Therefore, when developing XAML layouts, content handling, and style application, use Debug build for quick testing, then before moving on, do the following:

  1. Perform a Release build.
  2. Watch Debug view in Visual Studio's Output tool during build, it might show errors or warnings that could be clues to possible problems.
  3. Test every control, page, etc to confirm they behave as expected.

The solution to the problem of syling Span elements within a parent Label is to:

  1. Apply styles per usual to the Span itself, whether through in-line Style, or through Binding.
  2. Ensure that Span supported properties are applied (and not Label properties).

So for example instead of:

<!-- Template View, within a Layout, with ResourceDictionary pointing to Styles.xaml -->
<Label LineBreakMode="NoWrap" Style="{Binding LabelStyle}">
    <Label.FormattedText>
        <FormattedString>
            <Span Text="Hello "
                  Style="{StaticResource LabelStyle}" />
            <Span Text="{Binding World}"
                  Style="{StaticResource LabelStyle}"
                  />
        </FormattedString>
    </Label.FormattedText>
</Label>

<!-- Styles.xaml showing only the SPAN and LABEL element Style definitions -->
<Style TargetType="Label" x:Key="LabelStyle">
  <Setter Property="VisualStateManager.VisualStateGroups">
    <!-- defined visual state groups that SPAN does not support -->
  </Setter>
</Style>

...add a Span-specific styling and avoid relying on Label Styling, like this:

<!-- Template View, within a Layout, with ResourceDictionary pointing to Styles.xaml -->
<Label LineBreakMode="NoWrap">
    <Label.FormattedText>
        <FormattedString>
            <Span Text="Hello "
                  Style="{StaticResource SpanForecastItem}" />
            <Span Text="{Binding World}"
                  Style="{StaticResource SpanForecastItem}"
                  />
        </FormattedString>
    </Label.FormattedText>
</Label>

<!-- Styles.xaml showing only the SPAN and LABEL element Style definitions -->
<Style TargetType="Label">
  <Setter Property="VisualStateManager.VisualStateGroups">
    <!-- defined visual state groups that SPAN does not support -->
  </Setter>
</Style>
<Style TargetType="Span" x:Key="SpanStyle">
  <Setter Property="FontSize" Value="14" />
  <!-- more SPAN specific setters here -->
</Style>

Elements Span and Label do not share Styling properties, despite there being some overlap, so explicit bindings are required even through Debug Build will ignore the error, but Release Build and an actual Android platform deployment might not.

That completes the Forecast page style fix-up for the app. Next steps include:

Week 3

Watched a MSFT Reactor presentation today on continous integration (CI) with LLMs and AI Models. There were two guests with the host, and one of them mentioned Vector Databases and briefly described it.

Here are my notes about vector database and MSFT's Semantic Kernel.

Also see About Machine Learning for somewhat related notes from a previous MSFT Reactor session.

Git Console Commands and Flow

Other Git commands I rarely use:

Note: The posh-git repository is somewhat stale (2-years since last update/fix/response). This could mean it could fall out of compliance with newer PowerShell releases (currently I'm using 7.4.1).

Also note: After installing git (I usually select GIT-SCM latest), access the help files in the installation directory ./share/doc/git-doc/, or by typing git help for an overview of commands, and git help {topic} for a rich (html) manual.

MobWX Navigation Bug Fixups

I completed sorting out the issues with navigation in the mobile weather app. Also, the NWS managed to fix their 'Points Forecast' endpoint, but it has not been reliable, so occasionally there are REST results codes 404 and 5xx that my app will need to be better an handling.

There is more work to do, but the build is functional, publishing an APK works, and running on Windows and Android (both emulated and side-loaded) function without errors now.

The current version with navigation bug fixes is merged into main now and an updated side-loadable APK has been published privately.

About Using Public APIs

As I have worked through using the NWS public API over the last two months, I've been learning how to better deal with user inputs, and less-expected (or unexpected) API responses.

An few takeaways:

  1. Always look at the API documentation for updates about its operating state, especially known issues. The API publisher might have succinct conditional information that can be transformed into code to work around a problem. The publisher might also include information like "just try again and it should work". This research will reduce frustration trying to solve non-code problems by editing/refactoring code.
  2. API Key protection is difficult within a distributable app. At Code Fellows training they pushed the idea of relying on a custom API server to basically relay/proxy requests to actual APIs (and perhaps cache them). At the time I thought this was a convenient way to enforce learning how to build client-server architecture (probably still true), however a more important takeaway is: It is much simpler (and obvious how) to hide secrets like API Keys at the server level than it is to do it within a client itself. I'm sure there are simpler ways to hide secrets within an app, but I haven't gone deep into that rabbit hole yet.
  3. Leveraging a custom API proxy server allows separating the vagaries of API web-request-response-cycle (WRRC) transactions from the client app, so the client can concentrate on user experience (UX), while the server-side deals with data processing, handling errors, managing partial or incomplete API-call-chain responses, etc. This will also make the client-side code smaller.
  4. When relying on environment variables, always check that a value is actually returned and that the value isn't an empty string. Do this very early in the code so that no successful API calls are wasted when an invalid API call (lacking an environment variable) exits a multi-call chain of events. In other words: Identify and handle failure points as early as possible.

Week 1

MAUI Color and Theming

Working through implementing a usable About page for MobWxApp:

<!-- from .NET MAUI 8 documentation at learn.microsoft.com -->
<Label>
    <Label.FormattedText>
        <FormattedString>
            <Span Text="Alternatively, click " />
            <Span Text="here"
                  TextColor="Blue"
                  TextDecorations="Underline">
                <Span.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding TapCommand}"
                                          CommandParameter="https://learn.microsoft.com/dotnet/maui/" />
                </Span.GestureRecognizers>
            </Span>
            <Span Text=" to view .NET MAUI documentation." />
        </FormattedString>
    </Label.FormattedText>
</Label>
<!-- Avoid using SPAN elements -->
<Label Text=".NET MAUI Project Documentation"
       TextColor="DarkBlue"
       TextDecorations="Underline"
       VerticalAlignment="Center"
       >
  <Label.GestureRecognizers>
    <TapGestureRecognizer Command="{Binding TapCommand}"
                          CommandParameter="https://learn.microsoft.com/dotnet/maui/" />
  </Label.GestureRecognizers>
</Label>

The problematic C# Code uses Launcher.OpenAsync(uri) to navigate to a page:

using System.Windows.Input;
public partial class MainPage : ContentPage
{
    // Launcher.OpenAsync is provided by Essentials.
    public ICommand TapCommand => new Command<string>(async (url) => await Launcher.OpenAsync(url));
    public MainPage()
    {
        InitializeComponent();
        BindingContext = this;
    }
}

...what is really necessary for an external hyperlink is a Browser method to call the uri using the 'System Preferred' web browser:

// note: this could be done using ICommand but my implementation uses 
// the MVVM CommunityToolkit so I went with IAsyncRelayCommand instead
public partial class Mainpage : ContentPage
{
  public IAsyncRelayCommand<string> TapCommand => 
    new AsyncRelayCommand<string>(
        async (url) => await BrowserOpen(url)
        );
}
...
private async Task BrowserOpen(string url) {
  // check for null/whitespace string and open a try-catch block, then:
  try 
  {
    Uri uri = new Uri(url);
    bool result = await Browser.Default.OpenAsync(uri, BrowserLaunchMode.SystemPreferred);
  }
  catch (Exception ex)
  {
    // handle, notify, etc
  }
}

Theming In Particular:

Link-Like Label Styling:

The code I implemented for launching the browser and displaying a "link"-like Label are functional on Windows and Android (emulator API 32+).

Note: IBrowser.OpenAsync() documentation does not mention any Exception type that might get thrown.

Custom Images and Icons:

Miro is really helpful creating materials for images and icons. Some things to keep in mind when creating materials for .NET MAUI 8:

Publishing a Private Android APK using Visual Studio

So many times I've done this and yet the process is just un-obvious enough that I stumble through it pretty much everytime. The goal here is to document it so that I no longer need to look it up. :smiley:

  1. Build the MAUI App using Rebuild on the Solution.
  2. Select the Android emulator and run the app, confirming there are no errors then close the app.
  3. Select the target emulator for Android in the Debug configuration.
  4. Select Release in the Solution Configuration.
  5. Select Publish on the Project to deploy. If there is already a Publish Configuration, a build cycle will execute, otherwise the configuration must be set first.
  6. When the Archive Manager is done creating and packaging the APK, look at the bottom of the screen for Distribute... and click it to open the 'Distribute - Select Channel' window.
  7. Click Ad Hoc.
  8. Add a Signing Authority (have a secure password ready), or select an existing one.
  9. Click Save As to save the APK. Note: If there is already in an APK in that folder be certain to overwrite it otherwise the new deployment will not complete successfully.
  10. Confirm Overwrite file? and then enter the secure password.
  11. When that process completes, review the screen for any errors or problems.
  12. If there were no problems, click Open Distribution at the bottom of the Archive Manager window to gain access to an APK file that can be side-loaded onto an appropriate Android API Level phone.

Note: Select Open Folder to see the signed-apks folder, archive.xml, and deployable APK file.

Week 1 JavaScript Fun

Areas where I've been struggling with JavaScript recently: Arrow Functions!

// Functional "class"
const MyThing = function() {
  this.kvpStore = {};
  this.has = (key) => {
    return this.kvpStore.hasOwnProperty(key);
  };
}

I need to sort this out in my head so it is less frustrating next time:

// anonymous function
(function (num) {
  return num / 100; 
});

// basic arrow function removes keyword 'function' and parens and braces not necessary for one-line code block and single (simple) params
num => num / 100;

// braces and 'return' keyword required for multi-line code blocks
num => {
  const temp = num / 100;
  return temp + 100;
};

Note: The above examples are slightly modified versions from [MDN Javascript Reference], accessed 5-Jan-24.

// class method syntax example with public function definitions
const obj = {
  foo() {
    return 'bar';
  },
};

// the slightly longer form of the above:
const obj = {
  foo: function () {
    return 'bar';
  },
};

JavaScript Delete Operator

This is an odd one! Delete operator allows removing a property from an Object. Identify the object and property to perform the removal.

var HashTable = function() {
  this.collection = {}; // a key-value pair storage i.e. [hashcode, value]
  this.remove = (key) => {
    delete this.collection[key];
  }
  // add, has, and other functions...
}

Return to Root README