<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[blog.petefowler.dev]]></title><description><![CDATA[Software development blog focused on sharing knowledge, solutions to common problems, and news related to web development, JavaScript, Ruby, Node.js, React.js, CSS, HTML, etc.]]></description><link>https://blog.petefowler.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 07 Sep 2026 00:15:40 GMT</lastBuildDate><atom:link href="https://blog.petefowler.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The "best" git log command]]></title><description><![CDATA[git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
It gives you a graph, commit hash (red), decorations like branch names (yellow), commit subject / message (white), relativ...]]></description><link>https://blog.petefowler.dev/the-best-git-log-command</link><guid isPermaLink="true">https://blog.petefowler.dev/the-best-git-log-command</guid><category><![CDATA[Git]]></category><category><![CDATA[terminal]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Mon, 05 May 2025 13:20:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746451083726/e463918d-1a53-45bd-98d4-13e0aacd76c2.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1746450701211/c5adcb41-0f9b-411c-975d-3f18c77da10d.png" alt class="image--center mx-auto" /></p>
<p><code>git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit</code></p>
<p>It gives you a graph, commit hash (red), decorations like branch names (yellow), commit subject / message (white), relative time (green), and author name (blue, wrapped in &lt; &gt;).</p>
<p>Make it an alias so you can just type git lg:<br /><code>git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit"</code></p>
<p>Of course there are definitely nicer looking options in VS Code, VS Code extensions, stand alone Git GUIs, etc., but this is one of the best git log options to just run it from the terminal.</p>
]]></content:encoded></item><item><title><![CDATA[How to solve the Leetcode 1657. Determine if Two Strings Are Close problem in JavaScript]]></title><description><![CDATA[The Problem
This problem is described:

Two strings are considered close if you can attain one from the other using the following operations:

Operation 1: Swap any two existing characters.

For example, abcde -> aecdb


Operation 2: Transform every ...]]></description><link>https://blog.petefowler.dev/how-to-solve-the-leetcode-1657-determine-if-two-strings-are-close-problem-in-javascript</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-solve-the-leetcode-1657-determine-if-two-strings-are-close-problem-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[leetcode]]></category><category><![CDATA[array]]></category><category><![CDATA[hashmap]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Fri, 02 Dec 2022 17:50:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1670002660315/5e4feab3-252f-47c6-abc5-11b90908a348.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-the-problem">The Problem</h1>
<p><a target="_blank" href="https://leetcode.com/problems/determine-if-two-strings-are-close/">This problem</a> is described:</p>
<blockquote>
<p>Two strings are considered <strong>close</strong> if you can attain one from the other using the following operations:</p>
<ul>
<li><p>Operation 1: Swap any two <strong>existing</strong> characters.</p>
<ul>
<li>For example, <code>abcde -&gt; aecdb</code></li>
</ul>
</li>
<li><p>Operation 2: Transform <strong>every</strong> occurrence of one <strong>existing</strong> character into another <strong>existing</strong> character, and do the same with the other character.</p>
<ul>
<li>For example, <code>aacabb -&gt; bbcbaa</code> (all <code>a</code>'s turn into <code>b</code>'s, and all <code>b</code>'s turn into <code>a</code>'s)</li>
</ul>
</li>
</ul>
<p>You can use the operations on either string as many times as necessary.</p>
<p>Given two strings, <code>word1</code> and <code>word2</code>, return <code>true</code> <em>if</em> <code>word1</code> <em>and</em> <code>word2</code> <em>are</em> <strong><em>close</em></strong>, and <code>false</code> <em>otherwise.</em></p>
<p><strong>Example 1:</strong></p>
<pre><code class="lang-plaintext">Input: word1 = "abc", word2 = "bca"
Output: true
Explanation: You can attain word2 from word1 in 2 operations.
Apply Operation 1: "abc" -&gt; "acb"
Apply Operation 1: "acb" -&gt; "bca"
</code></pre>
<p><strong>Example 2:</strong></p>
<pre><code class="lang-plaintext">Input: word1 = "a", word2 = "aa"
Output: false
Explanation: It is impossible to attain word2 from word1, or vice versa, in any number of operations.
</code></pre>
<p><strong>Example 3:</strong></p>
<pre><code class="lang-plaintext">Input: word1 = "cabbba", word2 = "abbccc"
Output: true
Explanation: You can attain word2 from word1 in 3 operations.
Apply Operation 1: "cabbba" -&gt; "caabbb"
Apply Operation 2: "caabbb" -&gt; "baaccc"
Apply Operation 2: "baaccc" -&gt; "abbccc"
</code></pre>
<p><strong>Constraints:</strong></p>
<ul>
<li><p><code>1 &lt;= word1.length, word2.length &lt;= 105</code></p>
</li>
<li><p><code>word1</code> and <code>word2</code> contain only lowercase English letters.</p>
</li>
</ul>
</blockquote>
<h1 id="heading-key-insights">Key Insights</h1>
<p>This problem does not seem that approachable until coming to the following two key insights:</p>
<ul>
<li><p><strong><em>You can freely reorder the existing letters</em></strong> in a string by swapping any two existing letters. This is done through operation 1 in the problem description. For example, <code>abcde -&gt; aecdb</code> .</p>
</li>
<li><p><strong><em>You can also freely reassign the frequencies that any letter occurs</em></strong>. This is done by transforming <strong>every</strong> occurrence of one <strong>existing</strong> character into another <strong>existing</strong> character, and doing the same with the other character. This is done with operation 2 in the problem description. For example, <code>aacabb -&gt; bbcbaa</code> , (all <code>a</code>'s turn into <code>b</code>'s, and all <code>b</code>'s turn into <code>a</code>'s).</p>
</li>
</ul>
<p>These insights are not easy to come by at first glance. A good way to approach something like this is to play around with the two operations with a few example strings. While doing this, think about questions like:</p>
<ul>
<li><p>What is the operation really doing?</p>
</li>
<li><p>Is there a more basic, more abstract way to describe what the operation is doing?</p>
</li>
</ul>
<p>If that doesn't work, luckily for this problem, the two insights are listed in the hints for the problem.</p>
<h1 id="heading-solution">Solution</h1>
<p>Since you can freely reorder the strings, and freely reassign the frequencies of the letters to other letters, this means that you just need to find a way to test whether the strings consist of the same letters, and whether the letter frequencies (separate from any particular letters) are the same for both strings.</p>
<p>Here is one way to solve the problem, annotated with explanations along the way:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> closeStrings = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">word1, word2</span>) </span>{
    <span class="hljs-comment">// If the strings are not the same length, return </span>
    <span class="hljs-comment">// false as there is no way they'll be considered </span>
    <span class="hljs-comment">// "close" per the problem description</span>
    <span class="hljs-keyword">if</span> (word1.length !== word2.length) 
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;

    <span class="hljs-comment">/* Process each string with the helper function 
    getData (see below). The data1 &amp; data2 variables get 
    assigned to an object returned by the function. That 
    object has an array of letters from the string and an 
    array of their frequencies, both sorted. The frequencies 
    are just an array of numbers, no longer associated with 
    any particular letter. */</span>
    <span class="hljs-keyword">const</span> data1 = getData(word1);
    <span class="hljs-keyword">const</span> data2 = getData(word2);

    <span class="hljs-comment">// Loop through the array of letters from data1, which </span>
    <span class="hljs-comment">// came from processing the first string. </span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; data1.ltrs.length; i++) {
        <span class="hljs-comment">/* if the sorted arrays of letters from both strings 
        do not match, return false, or if the sorted arrays of 
        frequencies do not match, return false */</span>
        <span class="hljs-keyword">if</span>(data1.ltrs[i] !== data2.ltrs[i] 
           || data1.times[i] !== data2.times[i]) 
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    <span class="hljs-comment">// Otherwise, return true, since the two strings </span>
    <span class="hljs-comment">// are considered "close"</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
};

<span class="hljs-comment">/* A helper function that is used above to process the 
strings and return an object {ltrs: [array of letters], 
times: [array of numbers representing frequencies that 
letters occurred in the string]} */</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getData</span>(<span class="hljs-params">word</span>) </span>{
    <span class="hljs-comment">/* Create an empty object. It will store letters from 
    a string as keys and the number of times they appear 
    in the string as values */</span>
    <span class="hljs-keyword">const</span> map = {};

    <span class="hljs-comment">// Loop through the string</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
        <span class="hljs-comment">// If the letter does not exist as a key in the </span>
        <span class="hljs-comment">// map object, add it with a value of zero</span>
        <span class="hljs-keyword">if</span>(!map[char]) map[char] = <span class="hljs-number">0</span>;
        <span class="hljs-comment">// Increase the value of the letter/key in the map </span>
        <span class="hljs-comment">// object by 1. This is recording the number of times </span>
        <span class="hljs-comment">// the letter occurs in the string</span>
        map[char]++;
    }

    <span class="hljs-comment">/* Return an object with the key ltrs containing an 
    array of the string's letters. The key times contains 
    an array of the number of times the letters occurred. 
    Times is just an array of numbers (the frequencies). 
    The point here is that we ultimately want to test if 
    the letters from both strings are the same. We will 
    also test if the frequencies - separate from and not 
    associated with any letters because they can be freely 
    reassigned - are also the same. */</span>
    <span class="hljs-keyword">return</span> {
        <span class="hljs-comment">// ltrs gets assigned the keys of the map object </span>
        <span class="hljs-comment">// (which are letters), sorted</span>
        <span class="hljs-attr">ltrs</span>: <span class="hljs-built_in">Object</span>.keys(map).sort(),
        <span class="hljs-comment">/* times gets the values of the map object 
        (frequencies), sorted. The callback is passed to 
        the sort function to sort numbers properly, because 
        sort tries to sort values after converting to 
        strings, which can cause errors when sorting numbers. */</span>
        <span class="hljs-attr">times</span>: <span class="hljs-built_in">Object</span>.values(map).sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b)
    }
}
<span class="hljs-comment">/* Note - the letters and frequencies are both sorted so 
that we can use a loop to compare whether they are the 
same as letters and frequencies in the other string above. 
Sorting makes it easy to loop through and check if the 
values of arrays for both strings are the same at the 
same index. */</span>
</code></pre>
<p>Here is the solution without the comments:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> closeStrings = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">word1, word2</span>) </span>{
    <span class="hljs-keyword">if</span> (word1.length !== word2.length) 
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;

    <span class="hljs-keyword">const</span> data1 = getData(word1);
    <span class="hljs-keyword">const</span> data2 = getData(word2);

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; data1.ltrs.length; i++) {
        <span class="hljs-keyword">if</span>(data1.ltrs[i] !== data2.ltrs[i] 
           || data1.times[i] !== data2.times[i]) 
            <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getData</span>(<span class="hljs-params">word</span>) </span>{
    <span class="hljs-keyword">const</span> map = {};

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> char <span class="hljs-keyword">of</span> word) {
        <span class="hljs-keyword">if</span>(!map[char]) map[char] = <span class="hljs-number">0</span>;
        map[char]++;
    }

    <span class="hljs-keyword">return</span> {
        <span class="hljs-attr">ltrs</span>: <span class="hljs-built_in">Object</span>.keys(map).sort(),
        <span class="hljs-attr">times</span>: <span class="hljs-built_in">Object</span>.values(map).sort(<span class="hljs-function">(<span class="hljs-params">a, b</span>) =&gt;</span> a - b)
    }
}
</code></pre>
]]></content:encoded></item><item><title><![CDATA[How to add Google maps and markers to a React site]]></title><description><![CDATA[To get started with Google maps, don't use the official Google documentation and NPM package. It's more difficult to use and the documentation is not great. The 3rd party google map react NPM package has 281,893 weekly downloads, whereas the NPM pack...]]></description><link>https://blog.petefowler.dev/how-to-add-google-maps-and-markers-to-a-react-site</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-add-google-maps-and-markers-to-a-react-site</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><category><![CDATA[google maps]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sat, 12 Nov 2022 18:50:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/p7pEokZap1o/upload/v1668278993494/T1ujWBXu8.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>To get started with Google maps, don't use the official Google documentation and NPM package. It's more difficult to use and the documentation is not great. The 3rd party google map react NPM package has 281,893 weekly downloads, whereas the NPM package referenced in the official Google documentation, @googlemaps/react-wrapper, has only 107,832 weekly downloads. </p>
<h1 id="heading-install-npm-package">Install NPM package</h1>
<p>First, install the NPM package with <code>npm i google-map-react</code>.</p>
<h1 id="heading-create-a-map-component">Create a map component</h1>
<p>There is code on the <a target="_blank" href="https://www.npmjs.com/package//google-map-react">NPM google map react page</a> to get started building a map component.</p>
<ul>
<li>Import the NPM package at the top of the map component with<code>import GoogleMapReact from 'google-map-react';</code> </li>
<li>Build a standard component with props of zoom and center</li>
<li>Add a prop for markers if desired</li>
<li>Use the  component and pass in the props</li>
</ul>
<pre><code><span class="hljs-keyword">import</span> GoogleMapReact <span class="hljs-keyword">from</span> <span class="hljs-string">'google-map-react'</span>;
<span class="hljs-keyword">import</span> style <span class="hljs-keyword">from</span> <span class="hljs-string">'./Map.module.css'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Map</span>(<span class="hljs-params">{center, zoom, markers}</span>)</span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{style.map}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">GoogleMapReact</span>
        <span class="hljs-attr">bootstrapURLKeys</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">key:</span> <span class="hljs-attr">process.env.REACT_APP_API</span> <span class="hljs-attr">as</span> <span class="hljs-attr">string</span> }}
        <span class="hljs-attr">defaultCenter</span>=<span class="hljs-string">{center}</span>
        <span class="hljs-attr">defaultZoom</span>=<span class="hljs-string">{zoom}</span>
      &gt;</span>
        {markers}
      <span class="hljs-tag">&lt;/<span class="hljs-name">GoogleMapReact</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre><p>The bootstrapURLKeys prop requires an API key. In the above example it is stored in an environment variable. Go to the <a target="_blank" href="https://console.cloud.google.com/">Google cloud console</a> to get one. It must be used in the front end here and would be insecure in the browser, but the Google cloud console allows you to restrict the API key to certain sites or IP addresses.</p>
<h1 id="heading-markers-component">Markers component</h1>
<p>Above, I am passing the variable <code>markers</code> as a prop to the map component. The markers variable is an array of marker components. They could be any React component. They need the props <code>lat</code> and <code>lng</code> in order to be placed on the map at the desired location. Below is an example of creating some markers from data fetched from a back end. The markers later get passed to the map component.</p>
<pre><code>useEffect(<span class="hljs-function">() =&gt;</span> {
    fetch(<span class="hljs-string">`/locations`</span>)
    .then(<span class="hljs-function"><span class="hljs-params">r</span> =&gt;</span> {
      <span class="hljs-keyword">if</span>(r.ok) {
        r.json().then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> setMarkers(data.map(<span class="hljs-function"><span class="hljs-params">loc</span> =&gt;</span> 
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Marker</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{loc.id}</span> 
            <span class="hljs-attr">id</span>=<span class="hljs-string">{loc.id}</span> 
            <span class="hljs-attr">name</span>=<span class="hljs-string">{loc.name}</span> 
            <span class="hljs-attr">lat</span>=<span class="hljs-string">{loc.coordinates.split(</span>',')[<span class="hljs-attr">0</span>]} 
            <span class="hljs-attr">lng</span>=<span class="hljs-string">{loc.coordinates.split(</span>',')[<span class="hljs-attr">1</span>]}
              // <span class="hljs-attr">lat</span> <span class="hljs-attr">and</span> <span class="hljs-attr">lng</span> <span class="hljs-attr">are</span> <span class="hljs-attr">just</span> <span class="hljs-attr">numbers</span> <span class="hljs-attr">for</span> <span class="hljs-attr">latitude</span> <span class="hljs-attr">and</span> <span class="hljs-attr">longitude</span>
          /&gt;</span></span>
        )));
      } <span class="hljs-keyword">else</span> {
        r.json().then(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> <span class="hljs-built_in">console</span>.log(err));
      }
    })
  }, [])
</code></pre><p>The markers are passed to the map component when it is called like this: </p>
<p><code>&lt;Map center={{lat: 39.725194, lng: -105.175531}} zoom={7} markers={markers}/&gt;</code> </p>
<p>The center prop is an object with <code>lat</code> and <code>lng</code> keys, and <code>zoom</code> is an integer. The map component must have a defined height and width, otherwise it will not appear. In this example, it is the <code>&lt;div&gt;</code> containing the <code>&lt;GoogleMapReact&gt;</code> component that has a set height and width.</p>
<h1 id="heading-what-it-looks-like">What it looks like</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1668524325463/RaD_W06Zw.jpg" alt="map.jpg" /></p>
<p>The markers can be customized to be anything, have modal windows appear when hovered, etc.</p>
]]></content:encoded></item><item><title><![CDATA[How to deploy a Ruby Sinatra Active Record app on Heroku]]></title><description><![CDATA[This post will walk through deploying a full stack app with a React front end and a Sinatra, ActiveRecord, and Ruby back end on Heroku.
PostgreSQL
Heroku does not work with SQLite, so your app uses SQLite, you have to switch to using PostgreSQL in pr...]]></description><link>https://blog.petefowler.dev/how-to-deploy-a-ruby-sinatra-active-record-app-on-heroku</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-deploy-a-ruby-sinatra-active-record-app-on-heroku</guid><category><![CDATA[Ruby]]></category><category><![CDATA[sinatra]]></category><category><![CDATA[activerecord]]></category><category><![CDATA[Heroku]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Wed, 12 Oct 2022 23:47:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1665614225555/9hNj2rdpe.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post will walk through deploying a full stack app with a React front end and a Sinatra, ActiveRecord, and Ruby back end on Heroku.</p>
<h1 id="heading-postgresql">PostgreSQL</h1>
<p>Heroku does not work with SQLite, so your app uses SQLite, you have to switch to using PostgreSQL in production. In your Gemfile in the root folder, remove <code>gem "sqlite"</code> and place it inside a development block so as not to use it for the deployment. Note: best practice would have been to use the same database type for both development and deployment.</p>
<pre><code>group :development <span class="hljs-keyword">do</span>
  gem <span class="hljs-string">"sqlite3"</span>, <span class="hljs-string">"~&gt; 1.4"</span>
  gem <span class="hljs-string">"pry"</span>, <span class="hljs-string">"~&gt; 0.14.1"</span>
  gem <span class="hljs-string">"rerun"</span>
end
</code></pre><p>Add a production block to the Gemfile specify what the app will use once deployed. <code>gem 'pg'</code> adds an interface for Ruby to the PostgreSQL database. I also specified my deployment to use the 'psych' gem less than version 4 due to an error I was getting <code>Psych::BadAlias: Unknown alias: default</code>. </p>
<pre><code>group :production <span class="hljs-keyword">do</span>
  gem <span class="hljs-string">'psych'</span>, <span class="hljs-string">'&lt; 4'</span>
  gem <span class="hljs-string">"pg"</span>
end
</code></pre><p>It would also be a best practice to specify what version of Ruby you are using in the Gemfile. After editing the Gemfile, run <code>bundle install</code>.</p>
<p>Then edit config/database.yml to tell the app to use the PostgreSQL database in deployment. I changed the production block to the following:</p>
<pre><code>production:
  adapter: postgresql
  <span class="hljs-attr">encoding</span>: unicode
  <span class="hljs-attr">database</span>: production
</code></pre><h1 id="heading-procfile">Procfile</h1>
<p>Create a new file in the root directory of the app called <code>Procfile</code>. Add the contents: <code>web: bundle exec rackup config.ru -p $PORT</code>.</p>
<h1 id="heading-heroku">Heroku</h1>
<p>Create an account and install the <a target="_blank" href="https://devcenter.heroku.com/articles/heroku-cli">Heroku CLI</a> if you haven't already. Next, go to Heroku.com, create a new app, import the repository from GitHub and deploy. I ran additional commands to run the database migration and then seed the database. Due to another error, I also removed <code>require 'pry'</code> from seeds.rb.</p>
<pre><code>heroku run rake db:migrate

heroku run rake db:seed
</code></pre><h1 id="heading-config-variables">Config Variables</h1>
<p>I also added a config variable in Heroku with an API key I was using to seed the database. These can be set through Heroku's website by clicking the app &gt; settings &gt; and then a reveal config vars button. You can then add in the key and value. I deleted this variable after seeding, and it doesn't appear in this photo.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665616120299/sUiCo3_Ng.jpg" alt="Screenshot 2022-10-12 170745.jpg" /></p>
<h1 id="heading-frontend">Frontend</h1>
<p>Once the back end build is successful and you verify this by browsing to the API endpoints your front end is using, deploy the front end somewhere. Netlify is a good choice. You must make sure the URLs of all fetch requests in the front end go to the URL of the Heroku back end.</p>
<p>One way to do this instead of just hard coding in the URL is to use environment variables. In the root of the React front end, you can create two new files with these exact names: <code>.env.development</code> and <code>.env.production</code>.</p>
<p>In each file, assign a variable to the URL. The variable names must start with <code>REACT_APP_</code> and there are no quotation marks around the string/URL. Mine looked like this:</p>
<ul>
<li>.env.development<pre><code>REACT_APP_URL=http:<span class="hljs-comment">//localhost:9292</span>
</code></pre></li>
<li>.env.production <pre><code>REACT_APP_URL=https:<span class="hljs-comment">//yelpcloneserver.herokuapp.com/</span>
</code></pre></li>
</ul>
<p>Then, anywhere in the front end you use a fetch, you can access the proper URL with <code>process.env.REACT_APP_URL</code>. React will determine which version of the URL to use based on whether the app is in development mode or production mode. The environment variable can be stored in a shorter variable to make the fetch URL look cleaner:</p>
<pre><code><span class="hljs-comment">// Inside a React component</span>

<span class="hljs-keyword">const</span> url = process.env.REACT_APP_URL;

<span class="hljs-comment">// code ...</span>

fetch(<span class="hljs-string">`<span class="hljs-subst">${url}</span>/business/<span class="hljs-subst">${id}</span>`</span>)
  <span class="hljs-comment">// more code ....</span>
</code></pre><h1 id="heading-debug">Debug</h1>
<p>If this is the first time you are doing this, it is exceedingly likely you will run into errors. Don't give up, check the logs on Heroku and any error messages, and start researching them on Google and Stack Overflow.</p>
<h1 id="heading-links">Links</h1>
<p>Heroku docs:</p>
<ul>
<li><a target="_blank" href="https://devcenter.heroku.com/articles/getting-started-with-ruby">Getting started on Heroku with Ruby</a></li>
<li><a target="_blank" href="https://devcenter.heroku.com/articles/rack">Sinatra on Heroku</a></li>
<li><a target="_blank" href="https://devcenter.heroku.com/articles/sqlite3">Heroku's statement on SQLite</a></li>
</ul>
<p>Other guides:</p>
<ul>
<li><a target="_blank" href="https://www.codementor.io/@populardemand/the-heroku-procfile-1sxnqu1rqo">The Heroku Procfile</a></li>
<li><a target="_blank" href="https://www.linkedin.com/pulse/host-your-sinatra-app-heroku-trevor-tarpinian/">Host your Sinatra app on Heroku</a></li>
<li><a target="_blank" href="https://medium.com/@isphinxs/deploying-a-sinatra-app-to-heroku-7944b024f77c">Deploying a Sinatra app to Heroku</a></li>
<li><a target="_blank" href="https://dev.to/jtswisher/ruby-sinatra-app-heroku-deploy-3oc8">Ruby Sinatra app and Heroku depoy</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to make a star rating display in React that's better than the one on yelp.com]]></title><description><![CDATA[This post will discuss how to create a star rating display in React, and a star rating picker with color-changing hover effects similar to Yelp.com's version.
Yelp's star rating display rounds to the half star:


Yelp has over 5 million claimed busin...]]></description><link>https://blog.petefowler.dev/how-to-make-a-star-rating-display-in-react-thats-better-than-the-one-on-yelpcom</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-make-a-star-rating-display-in-react-thats-better-than-the-one-on-yelpcom</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[React]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Mon, 10 Oct 2022 21:45:39 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/0ZUoBtLw3y4/upload/v1665438279243/SJWmSvb4O.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>This post will discuss how to create a star rating display in React, and a star rating picker with color-changing hover effects similar to Yelp.com's version.</p>
<p>Yelp's star rating display rounds to the half star:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665421120449/RCz2XUrDk.jpg" alt="yelpfourthree.jpg" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665421130910/3yFGoKcQR.jpg" alt="yelpfourtwo.jpg" /></p>
<p>Yelp has over 5 million claimed businesses. Assuming an even distribution of average star ratings, rounding down from .2 and up from .8 suggests Yelp has over 2 million worth of fractions of stars either taken away or falsely awarded.</p>
<p>The version we will build here rounds to the tenth. It could go even further than this, but it wouldn't make a difference visually:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665421200755/laaxZD766.jpg" alt="starratingfraction.jpg" /></p>
<h1 id="heading-star-rating-display">Star Rating Display</h1>
<h2 id="heading-overview">Overview</h2>
<ul>
<li>Get the average star rating. This will be converted to an array.</li>
<li>Calculate the number of full stars, the decimal/fraction of the partial star, and the number empty stars </li>
<li>Push them into an array of 5, with 1s for full stars, a decimal for the partially filled star (like 0.3), and 0s for empty stars</li>
<li>Map the array into divs and set the background color to a linear gradient that changes from your fill color (orange here), to an empty color (gray), based on the values in the array</li>
<li>The 1s in the array become 100% full of the fill color, the partial star becomes partially full of the fill color based on the decimal, and the empty stars are 0% full of the fill color and only colored with the empty color</li>
</ul>
<h2 id="heading-code">Code</h2>
<pre><code><span class="hljs-keyword">const</span> fullStars = <span class="hljs-built_in">Math</span>.floor(starAverage);
  <span class="hljs-comment">// Gets the number of full stars. starAverage is the rating, for example </span>
  <span class="hljs-comment">// if the rating were 4.3, fullStars would now be 4.</span>

<span class="hljs-keyword">const</span> starArr = [];
  <span class="hljs-comment">// Create an empty array. We will add 1s, 0s, and a decimal value for the </span>
  <span class="hljs-comment">// partial star.</span>

<span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">1</span>; i &lt;= fullStars; i++)
{
  starArr.push(<span class="hljs-number">1</span>);
}
  <span class="hljs-comment">// This adds a 1 to the array for each full star in our rating</span>

<span class="hljs-keyword">if</span>(starAverage &lt; <span class="hljs-number">5</span>) {
  <span class="hljs-comment">// Wrapped in an if block because the following only needs to occur if </span>
  <span class="hljs-comment">// it's not a full 5.</span>

  <span class="hljs-keyword">const</span> partialStar = starAverage - fullStars;
    <span class="hljs-comment">// Calculates the partial star. For example 4.3 - 4 = 0.3. 0.3 will get </span>
    <span class="hljs-comment">// added to the array in the next line to represent the partial star</span>

  starArr.push(partialStar);
    <span class="hljs-comment">// Adds the partial star to the array</span>

  <span class="hljs-keyword">const</span> emptyStars = <span class="hljs-number">5</span> - starArr.length;
    <span class="hljs-comment">// Calculates the number of empty stars</span>

  <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">1</span>; i&lt;=emptyStars; i++) {
    starArr.push(<span class="hljs-number">0</span>);
  }
    <span class="hljs-comment">// This for loop adds 0s to the array to represent empty stars</span>
}

<span class="hljs-keyword">const</span> stars = starArr.map(<span class="hljs-function">(<span class="hljs-params">val, i</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{i}</span> 
    <span class="hljs-attr">className</span>=<span class="hljs-string">{style.starBox}</span> 
    <span class="hljs-attr">style</span>=<span class="hljs-string">{{background:</span> `<span class="hljs-attr">linear-gradient</span>(<span class="hljs-attr">90deg</span>, #<span class="hljs-attr">ff643d</span> 
    ${<span class="hljs-attr">val</span> * <span class="hljs-attr">100</span>}%, #<span class="hljs-attr">bbbac0</span> ${<span class="hljs-attr">val</span> * <span class="hljs-attr">100</span>}%)`}}&gt;</span>★<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  })
  <span class="hljs-comment">// This last block is explained in the following paragraphs below</span>
</code></pre><p>The last block starting with <code>const stars =</code> takes the array of 1s, a decimal, and zeros we created and maps it to 5 div elements. The stars variable is then used later in the return statement of the React component where we want to show the star rating. The key to getting the color fill right is the style attribute. </p>
<p><code>style={{background: `linear-gradient(90deg, #ff643d ${val * 100}%, #bbbac0 ${val * 100}%)`}}&gt;★&lt;/div&gt;</code> is setting the background color of each div using a linear gradient. </p>
<p>The first argument, 90deg, means the gradient color change is happening from left to right across the div.  The next argument, <code>#ff643d ${val * 100}%</code>, is saying we start at the color orange, and continue right with solid orange until a certain percentage across. The percentage is calculated from <code>val</code>, which is the 1, decimal or zero coming from the starArr array. So if it is a 1, it colors orange 100% across the div for a full star. For a decimal, like 0.3, it will color orange 30% across the div. </p>
<p>The next argument, <code>#bbbac0 ${val * 100}</code>, is another color stop using gray, and the same calculated percentage. This just means that there is no smooth transition between colors because the percent value is the same as the last color stop, so there is an immediate change from orange to gray. Read the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/gradient/linear-gradient">MDN linear gradient docs</a> to learn more.</p>
<p>If the value getting mapped is a 0, then there will be no orange in the background of the div and only gray, because the switch from orange to gray happens at 0% of the way across the linear gradient.</p>
<h1 id="heading-star-rating-picker">Star Rating Picker</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1665436452702/J3v2o09dt.gif" alt="pickergif.gif" /></p>
<p>This is built in a similar way with some changes. The entire react component with commented explanations is below:</p>
<pre><code><span class="hljs-keyword">import</span> React, { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> style <span class="hljs-keyword">from</span> <span class="hljs-string">'./StarRatingPicker.module.css'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">StarRatingPicker</span>(<span class="hljs-params">{ rate, rating, changeColor, color, 
  parent }</span>) </span>{

  <span class="hljs-keyword">const</span> colors = [<span class="hljs-string">'#FFD56A'</span>, <span class="hljs-string">'#FFA448'</span>, <span class="hljs-string">'#ff7e42'</span>, <span class="hljs-string">'#ff523d'</span>, <span class="hljs-string">'#f43939'</span>];
    <span class="hljs-comment">// An array of colors going from yellow to dark red</span>

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> i = colors[<span class="hljs-built_in">Math</span>.floor(rating - <span class="hljs-number">1</span>)];
    changeColor(i)
  }, [])
    <span class="hljs-comment">// This sets the initial color to state based on any initial rating.</span>

  <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">hoverRating</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Select your rating"</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">1</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Not good"</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">2</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Could've been better"</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">3</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"OK"</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">4</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Good"</span>
    }
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (rating === <span class="hljs-number">5</span>) {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Great"</span>
    }
  }
    <span class="hljs-comment">// This provides text that goes with the different ratings</span>

  <span class="hljs-keyword">const</span> fullStars = <span class="hljs-built_in">Math</span>.floor(rating);
    <span class="hljs-comment">// Again calculating the number of full stars</span>

  <span class="hljs-keyword">const</span> starArr = [];

  <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i = <span class="hljs-number">1</span>; i &lt;= fullStars; i++)
  {
    starArr.push(<span class="hljs-number">1</span>);
  }
    <span class="hljs-comment">// Adding 1s to the starArr array to represent full stars</span>

    <span class="hljs-keyword">if</span>(starAverage &lt; <span class="hljs-number">5</span>) {
      <span class="hljs-keyword">const</span> partialStar = starAverage - fullStars;
      starArr.push(partialStar);
        <span class="hljs-comment">// Calculate and add the partial star to the array as a decimal value</span>

    <span class="hljs-keyword">const</span> emptyStars = <span class="hljs-number">5</span> - starArr.length;
      <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i=<span class="hljs-number">1</span>; i&lt;=emptyStars; i++) {
      starArr.push(<span class="hljs-number">0</span>);
        <span class="hljs-comment">// Calculate and add the empty stars to the array as zeros</span>
      }
    }

  <span class="hljs-keyword">const</span> starRatingPicker = starArr.map(<span class="hljs-function">(<span class="hljs-params">val, index</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>
      <span class="hljs-attr">className</span>=<span class="hljs-string">{style.starBox}</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> rate(index + 1)}
      onMouseEnter={() =&gt; {
        rate(index + 1);
        changeColor(colors[index]);
      }
          // Again mapping the array to divs as in the previous example. 
          // Here we add an onClick listener to call a rate function that 
          // sets a rating state, which is used in a post to the server to 
          // create the actual rating.
          // ChangeColor sets a color state from the colors array at the 
          // top of the component, which will be used below
    } 
    style={{background: `linear-gradient(90deg, ${color}, ${color} 
      ${val * 100}%, #bbbac0 ${val * 100}%)`}}&gt;★<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        <span class="hljs-comment">// Setting the background color of the div. This is the same as </span>
        <span class="hljs-comment">// the previous example, except instead of a hard-coded orange, </span>
        <span class="hljs-comment">// the color value from state is used</span>
  })

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{style.rating</span> + ' ' + <span class="hljs-attr">parent</span>}&gt;</span>{starRatingPicker}
    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{style.hoverText}</span>&gt;</span>{hoverRating()}<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
}
</code></pre>]]></content:encoded></item><item><title><![CDATA[Capturing multi-line console output in Ruby's RSpec testing interface]]></title><description><![CDATA[How do you capture multiple lines of console output in Ruby? Here is one way to do it. This example comes from writing a test for multiple lines of output from a command-line tic-tac-toe game, where the board should print into the console on 5 differ...]]></description><link>https://blog.petefowler.dev/capturing-multi-line-console-output-in-rubys-rspec-testing-interface</link><guid isPermaLink="true">https://blog.petefowler.dev/capturing-multi-line-console-output-in-rubys-rspec-testing-interface</guid><category><![CDATA[Ruby]]></category><category><![CDATA[#rspec]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sun, 02 Oct 2022 18:32:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/NigObFIOsKQ/upload/v1664735104560/9_A1lWr1A.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>How do you capture multiple lines of console output in Ruby? Here is one way to do it. This example comes from writing a test for multiple lines of output from a command-line tic-tac-toe game, where the board should print into the console on 5 different lines. </p>
<h2 id="heading-solution">Solution</h2>
<pre><code>  describe <span class="hljs-string">"#print_board"</span> <span class="hljs-keyword">do</span> 
    it <span class="hljs-string">"prints current state of gameboard"</span> <span class="hljs-keyword">do</span> 
      game.board = [<span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>]

      $stdout = StringIO.new
      game.print_board
      $stdout.rewind

      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" X | O | X \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
    end
  end
</code></pre><h2 id="heading-ruby-docorghttpsruby-docorg-definitions"><a href="https://ruby-doc.org/">Ruby-Doc.org</a> Definitions</h2>
<ul>
<li><code>$stdout</code> - The current standard output</li>
<li><code>STDOUT</code> - The standard output. It is the default value for <code>$stdout</code></li>
<li><code>StringIO</code> - A class that is a "psuedo I/O on a string object."</li>
</ul>
<h2 id="heading-what-does-any-of-this-actually-mean-lets-help-ruby-docorg-out-a-bit">What does any of this actually mean? Let's help Ruby-Doc.org out a bit</h2>
<ul>
<li><code>$stdout</code> - It's where the output currently goes. It can be reassigned to something else. If you reassign it to a file or a StringIO object, the output is now going there. </li>
<li><code>STDOUT</code> - This is where output goes at the time a Ruby process was launched. It is the default value for <code>$stdout</code> and unless <code>$stdout</code> is reassigned they are essentially the same. This is good because you can reassign <code>$stdout</code> to STDOUT to undo any reassignment you have made. </li>
<li><code>StringIO</code> - You can create a StringIO object from this class and do a number of things with it including write puts to it, or gets strings from it</li>
</ul>
<h2 id="heading-solution-breakdown">Solution breakdown</h2>
<p>The entire solution again: </p>
<pre><code>  describe <span class="hljs-string">"#print_board"</span> <span class="hljs-keyword">do</span> 
    it <span class="hljs-string">"prints current state of gameboard"</span> <span class="hljs-keyword">do</span> 
      game.board = [<span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>, <span class="hljs-string">"X"</span>, <span class="hljs-string">"O"</span>]

      $stdout = StringIO.new
      game.print_board
      $stdout.rewind

      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" X | O | X \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
    end
  end
</code></pre><h2 id="heading-code-breakdown">Code breakdown</h2>
<pre><code>$stdout = StringIO.new
</code></pre><p>Assigns $stdout to a new StringIO object. Any console output is now being saved to a new StringIO object that $stdout points to.</p>
<pre><code>game.print_board
</code></pre><p>This prints the board to the console.</p>
<pre><code>$stdout.rewind
</code></pre><p>This calls a .rewind method available from the StringIO class (of which $stdout is pointing to a new instance of).
This works basically like a tape, so our StringIO instance has been rewound and is at the beginning of the string input we captured.</p>
<pre><code>      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" X | O | X \n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">"-----------\n"</span>)
      expect($stdout.gets).to eq(<span class="hljs-string">" O | X | O \n"</span>)
</code></pre><p><code>$stdout.gets</code> simply gets the first line of the output we've captured. Any additional <code>$stdout.gets</code> will just get the next line, and then the one after that.
Each string it received included <code>'\n'</code>, designating a line break within a string, so the <code>'\n'</code> was included for the test to pass.</p>
<h2 id="heading-reference">Reference</h2>
<ul>
<li><a href="https://ruby-doc.org/stdlib-2.5.1/libdoc/stringio/rdoc/StringIO.html#method-i-rewind">Ruby-Doc.org</a></li>
<li><a href="https://stackoverflow.com/questions/17709317/how-to-test-puts-in-rspec">Stack Overflow: How to test puts in RSpec</a></li>
<li><a href="https://stackoverflow.com/questions/6671716/difference-between-stdout-and-stdout-in-ruby">Stack Overflow: Difference between $stdout and STDOUT in Ruby</a></li>
<li><a href="https://rspec.info/">RSpec</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[A quick guide to cell phone vibration with JavaScript]]></title><description><![CDATA[Let's break down how to make a cell phone vibrate using JavaScript. A browser-based game is a great (the best?) use case for this. For example, in a Battleship game, the phone could vibrate when the ship is sunk, making it that much better. Another e...]]></description><link>https://blog.petefowler.dev/a-quick-guide-to-cell-phone-vibration-with-javascript</link><guid isPermaLink="true">https://blog.petefowler.dev/a-quick-guide-to-cell-phone-vibration-with-javascript</guid><category><![CDATA[Mobile Development]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Android]]></category><category><![CDATA[iOS]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sun, 25 Sep 2022 23:33:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/unsplash/o0A5BpHxziU/upload/v1664144195872/PLIAR9GFI.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Let's break down how to make a cell phone vibrate using JavaScript. A browser-based game is a great (the best?) use case for this. For example, in a Battleship game, the phone could vibrate when the ship is sunk, making it that much better. Another excellent use is for some haptic feedback when the user presses a button or checks a checkbox. This is actually very simple and uses something called the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Vibration_API">navigator interface</a>.</p>
<h1 id="heading-navigatorvibrate">navigator.vibrate()</h1>
<p>At the spot in our code where we want to cause the phone to vibrate, just use <code>navigator.vibrate(200);</code> The 200 represents the number of milliseconds the vibration should last, and can of course be changed. For multiple pulses in a pattern, pass in an array of values. The array defines alternating times that the phone vibrates, pauses, vibrates, and so on:</p>
<pre><code>navigator.vibrate([<span class="hljs-number">400</span>, <span class="hljs-number">300</span>, <span class="hljs-number">400</span>])
</code></pre><p>Here the phone would vibrate for 400ms, stop for 300ms, then vibrate again for 400ms. There can be as many vibration/pause pairs "as you would like," according to Mozilla Developer Network (MDN) docs. But don't get too crazy, less is more here. No one wants their phone constantly vibrating every time something happens in a game. For haptic feedback on a button press, a very short time such as 50ms is probably about right.</p>
<h1 id="heading-apple">Apple ...</h1>
<p>The unfortunate reality about the vibration API is that <strong><em>this will only work on Android phones</em></strong> because Apple does not support it. Here is a <a target="_blank" href="https://stackoverflow.com/questions/56926591/navigator-vibrate-break-the-code-on-ios-browsers">Stack Overflow comment</a> on the subject:</p>
<blockquote>
<blockquote>
<p>&gt;
From the developer prospective (trying to build an awesome website), it seems ridiculous that the vibration apis are not exposed for use on iOS; after all, it has been available in other browsers for almost 8 years. But when looked upon from Apple's perspective... think of all the terrible websites that would ruin your browsing experience by spamming vibration. They are smart enough to see that it would turn into another pop-up-ocalypse. And that is why we can't have nice things.  -isaacdre
&gt;</p>
</blockquote>
</blockquote>
<p>In fact, just calling navigator.vibrate() when the user is on an Apple iPhone <strong><em>causes the entire program to freeze</em></strong>. But it's not an insurmountable problem.</p>
<h1 id="heading-solution">Solution</h1>
<p>A crashing Apple device can be prevented by first checking whether the device supports the navigator.vibrate() interface. Above the location where we actually want the vibration to happen, we can write this:</p>
<pre><code><span class="hljs-keyword">let</span> canVibrate = <span class="hljs-literal">false</span>;
<span class="hljs-keyword">if</span>(<span class="hljs-string">'vibrate'</span> <span class="hljs-keyword">in</span> navigator)
  canVibrate = <span class="hljs-literal">true</span>;
</code></pre><p>We are creating a canVibrate variable that is set to false. It is then set to true only if the device supports the vibration interface. Then, at the spot where we want to cause the vibration, simply wrap it in an if block that checks if canVibrate is true:</p>
<pre><code>  <span class="hljs-keyword">if</span> (canVibrate) 
    navigator.vibrate(<span class="hljs-number">500</span>);
</code></pre><p>What happens is that this will cause the vibration on Android phones, and do nothing on iPhones. The iPhone won't vibrate, but it also won't crash the website.</p>
<h1 id="heading-stop-vibration">Stop vibration</h1>
<p>It's possible to stop any ongoing vibrations by calling <code>navigator.vibrate(0);</code> However, it seems like a bad idea to be causing so much vibration or using setInterval() with this that we would need to stop the vibration. </p>
<h1 id="heading-what-is-navigator">What is navigator?</h1>
<p>MDN says</p>
<blockquote>
<blockquote>
<p>&gt;
The Navigator interface represents the state and the identity of the user agent. It allows scripts to query it and to register themselves to carry on some activities. A Navigator object can be retrieved using the read-only window.navigator property.
&gt;</p>
</blockquote>
</blockquote>
<p>The navigator interface also gives us a number of other things like <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/Navigator/geolocation">navigator.geolocation</a> that can be used to get a geolocation object and use the location of the device.</p>
<h1 id="heading-tldr">TL;DR</h1>
<p>Use navigator.vibrate(500) (or another number of milliseconds) to cause a vibration on a mobile device. But it will crash an iPhone since Apple doesn't support the interface, so it's best write code that first checks if the interface is supported.</p>
]]></content:encoded></item><item><title><![CDATA[How to compare arrays in JavaScript]]></title><description><![CDATA[array1 === array2 ?
To compare arrays in JavaScript, don't assume you can check whether the contents of two JavaScript arrays are the same with === or == (the strict equality or equality operators). You can't.
const array1 = [1, 2, 3];
const array2 =...]]></description><link>https://blog.petefowler.dev/how-to-compare-arrays-in-javascript</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-compare-arrays-in-javascript</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[arrays]]></category><category><![CDATA[Recursion]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sat, 03 Sep 2022 16:58:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1662224185350/EatetmDyZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1662213596464/cqky2Kw9II.jpg" alt="array.jpg" /></p>
<h1 id="heading-array1-array2">array1 === array2 ?</h1>
<p>To compare arrays in JavaScript, don't assume you can check whether the contents of two JavaScript arrays are the same with <code>===</code> or <code>==</code> (the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality">strict equality</a> or <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality">equality</a> operators). You can't.</p>
<pre><code><span class="hljs-string">const</span> <span class="hljs-string">array1</span> <span class="hljs-string">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]<span class="hljs-string">;</span>
<span class="hljs-string">const</span> <span class="hljs-string">array2</span> <span class="hljs-string">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]<span class="hljs-string">;</span>

<span class="hljs-string">array1</span> <span class="hljs-string">===</span> <span class="hljs-string">array2</span>    <span class="hljs-string">//</span> <span class="hljs-string">returns</span> <span class="hljs-literal">false</span>
</code></pre><p>The code above is checking if the above two variables refer to the same array instance, and not whether their contents are the same. It would only return true for something like this:</p>
<pre><code>const array1 <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>]
const array2 <span class="hljs-operator">=</span> array1;     <span class="hljs-comment">// both variables point to the same array instance</span>

array1 <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> array2  <span class="hljs-comment">// now returns true</span>
</code></pre><h1 id="heading-solutions">Solutions</h1>
<h3 id="heading-jsonstringify">JSON.stringify()</h3>
<p>One way to solve the problem is to use <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify">JSON.stringify()</a>, which converts a value to a JSON string.</p>
<pre><code>const array1 <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
const array2 <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];

JSON.stringify(array1) <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> JSON.stringify(array2)   <span class="hljs-comment">// returns true</span>
</code></pre><p>JSON.stringify even works with multiple levels of nesting:</p>
<pre><code><span class="hljs-string">const</span> <span class="hljs-string">array1</span> <span class="hljs-string">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, {<span class="hljs-attr">x:</span> <span class="hljs-number">5</span>}, [<span class="hljs-number">1</span>, {<span class="hljs-attr">y:</span> <span class="hljs-number">7</span>}, <span class="hljs-number">3</span>]]<span class="hljs-string">;</span>
<span class="hljs-string">const</span> <span class="hljs-string">array2</span> <span class="hljs-string">=</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, {<span class="hljs-attr">x:</span> <span class="hljs-number">5</span>}, [<span class="hljs-number">1</span>, {<span class="hljs-attr">y:</span> <span class="hljs-number">7</span>}, <span class="hljs-number">3</span>]]<span class="hljs-string">;</span>

<span class="hljs-string">JSON.stringify(array1)</span> <span class="hljs-string">===</span> <span class="hljs-string">JSON.stringify(array2);</span>    <span class="hljs-string">//</span> <span class="hljs-string">returns</span> <span class="hljs-literal">true</span>
</code></pre><p>However, this method is not perfect. It is dependent on the JSON.stringify() method implementation not changing. There are also edge cases that produce strange behavior. Undefined is not a valid JSON value, so JSON.stringify() converts undefined to null, and this method could falsely return true in that case:</p>
<pre><code>const array1 <span class="hljs-operator">=</span> [null, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];
const array2 <span class="hljs-operator">=</span> [undefined, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>];


JSON.stringify(array1) <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> JSON.stringify(array2)
  <span class="hljs-comment">// returns true, should be false</span>

null <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> undefined     
  <span class="hljs-comment">// returns false</span>
</code></pre><h3 id="heading-arrayevery">array.every()</h3>
<p>Another method uses array.every(). It first checks if the arrays are the same length, and then checks that each value in the first equals the value in the second at the same index:</p>
<pre><code>const isEqual <span class="hljs-operator">=</span> array1.<span class="hljs-built_in">length</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> array2.<span class="hljs-built_in">length</span> 
<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> array1.every((value, index) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> value <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> array2[index])

console.log(isEqual);
</code></pre><p>However, the array.every() way won't work for arrays nested with additional arrays or objects, which would need to be checked recursively. </p>
<h3 id="heading-recursive-methods">Recursive methods</h3>
<p>This can be done with the isEqual method from the <a target="_blank" href="https://lodash.com/">Lodash</a> library. According to <a target="_blank" href="https://gist.github.com/jsjain/a2ba5d40f20e19f734a53c0aad937fbb">this gist</a>, it is doing something under the hood like the following:</p>
<pre><code>const array1 <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, [<span class="hljs-number">1</span>, [{a: <span class="hljs-string">'b'</span>}]], <span class="hljs-number">3</span>];
const array2 <span class="hljs-operator">=</span> [<span class="hljs-number">1</span>, [<span class="hljs-number">1</span>, [{a: <span class="hljs-string">'b'</span>}]], <span class="hljs-number">3</span>];

const isEqual <span class="hljs-operator">=</span> (first, second) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  <span class="hljs-keyword">if</span> (first <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> second) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
  }
  <span class="hljs-keyword">if</span> ((first <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> undefined <span class="hljs-operator">|</span><span class="hljs-operator">|</span> second <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> undefined 
<span class="hljs-operator">|</span><span class="hljs-operator">|</span> first <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> null <span class="hljs-operator">|</span><span class="hljs-operator">|</span> second <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> null) <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> (first <span class="hljs-operator">|</span><span class="hljs-operator">|</span> second)) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  const firstType <span class="hljs-operator">=</span> first?.constructor.<span class="hljs-built_in">name</span>;
  const secondType <span class="hljs-operator">=</span> second?.constructor.<span class="hljs-built_in">name</span>;
  <span class="hljs-keyword">if</span> (firstType <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> secondType) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  <span class="hljs-keyword">if</span> (firstType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'Array'</span>) {
    <span class="hljs-keyword">if</span> (first.<span class="hljs-built_in">length</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> second.<span class="hljs-built_in">length</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    let equal <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> first.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
      <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>isEqual(first[i], second[i])) {
        equal <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">break</span>;
      }
    }
    <span class="hljs-keyword">return</span> equal;
  }
  <span class="hljs-keyword">if</span> (firstType <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'Object'</span>) {
    let equal <span class="hljs-operator">=</span> <span class="hljs-literal">true</span>;
    const fKeys <span class="hljs-operator">=</span> Object.keys(first);
    const sKeys <span class="hljs-operator">=</span> Object.keys(second);
    <span class="hljs-keyword">if</span> (fKeys.<span class="hljs-built_in">length</span> <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> sKeys.<span class="hljs-built_in">length</span>) {
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    <span class="hljs-keyword">for</span> (let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> fKeys.<span class="hljs-built_in">length</span>; i<span class="hljs-operator">+</span><span class="hljs-operator">+</span>) {
      <span class="hljs-keyword">if</span> (first[fKeys[i]] <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> second[fKeys[i]]) {
        <span class="hljs-keyword">if</span> (first[fKeys[i]] <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> second[fKeys[i]]) {
          <span class="hljs-keyword">continue</span>; <span class="hljs-comment">// eslint-disable-line</span>
        }
        <span class="hljs-keyword">if</span> (first[fKeys[i]] <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> (first[fKeys[i]].constructor.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'Array'</span>
          <span class="hljs-operator">|</span><span class="hljs-operator">|</span> first[fKeys[i]].constructor.<span class="hljs-built_in">name</span> <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> <span class="hljs-string">'Object'</span>)) {
          equal <span class="hljs-operator">=</span> isEqual(first[fKeys[i]], second[fKeys[i]]);
          <span class="hljs-keyword">if</span> (<span class="hljs-operator">!</span>equal) {
            <span class="hljs-keyword">break</span>;
          }
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (first[fKeys[i]] <span class="hljs-operator">!</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> second[fKeys[i]]) {
          equal <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
          <span class="hljs-keyword">break</span>;
        }
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> ((first[fKeys[i]] <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> <span class="hljs-operator">!</span>second[fKeys[i]]) <span class="hljs-operator">|</span><span class="hljs-operator">|</span> 
          (<span class="hljs-operator">!</span>first[fKeys[i]] <span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> second[fKeys[i]])) {
        equal <span class="hljs-operator">=</span> <span class="hljs-literal">false</span>;
        <span class="hljs-keyword">break</span>;
      }
    }
    <span class="hljs-keyword">return</span> equal;
  }
  <span class="hljs-keyword">return</span> first <span class="hljs-operator">=</span><span class="hljs-operator">=</span><span class="hljs-operator">=</span> second;
};

console.log(isEqual(array1, array2));    <span class="hljs-comment">// returns true</span>
</code></pre><h1 id="heading-references">References</h1>
<ul>
<li><a target="_blank" href="https://www.30secondsofcode.org/articles/s/javascript-array-comparison">30secondsofcode.org</a></li>
<li><a target="_blank" href="https://flexiple.com/javascript/javascript-array-equality/">flexiple.com</a></li>
<li><a target="_blank" href="https://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript">Stack Overflow</a></li>
<li><a target="_blank" href="https://gist.github.com/jsjain/a2ba5d40f20e19f734a53c0aad937fbb">Shubham Jain's gist</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to create a parallax scroll effect using vanilla JavaScript and CSS]]></title><description><![CDATA[Depth and interactivity
Well done parallax scrolling effects can add incredible depth and interactivity to a web page. This simple example uses a fixed background image. The site content is stacked on top and scrolling over it, while transparent <div...]]></description><link>https://blog.petefowler.dev/how-to-create-a-parallax-scroll-effect-using-vanilla-javascript-and-css</link><guid isPermaLink="true">https://blog.petefowler.dev/how-to-create-a-parallax-scroll-effect-using-vanilla-javascript-and-css</guid><category><![CDATA[scrollY]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[CSS]]></category><category><![CDATA[parallax]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sun, 14 Aug 2022 17:53:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660499146220/pICCw5Bip.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-depth-and-interactivity">Depth and interactivity</h2>
<p>Well done parallax scrolling effects can add incredible depth and interactivity to a web page. This simple example uses a <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/position">fixed</a> background image. The site content is stacked on top and scrolling over it, while transparent <code>&lt;div&gt;</code> elements reveal the background. The background moves at 40% of the window scroll speed.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660498434892/qLoUh3LXl.gif" alt="parallaxSmall.gif" />
<a target="_blank" href="https://petefowler.dev/">Link to site</a></p>
<h2 id="heading-implementation">Implementation</h2>
<p>This works by stacking the fixed background image underneath the other elements by using a lower <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/z-index">z-index</a>. The revealing <code>&lt;div&gt;</code> elements act as a transparent window by using the <code>opacity: 0;</code> CSS property.</p>
<p>The parallax scroll effect of the background image is done with the following JavaScript:</p>
<pre><code>const background <span class="hljs-operator">=</span> document.querySelector(<span class="hljs-string">'#background'</span>);

const parallax <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  const { scrollY } <span class="hljs-operator">=</span> window;
  background.style.top <span class="hljs-operator">=</span> (scrollY <span class="hljs-operator">*</span> <span class="hljs-number">-.4</span>) <span class="hljs-operator">+</span> <span class="hljs-string">'px'</span>;
}
window.addEventListener(<span class="hljs-string">'scroll'</span>, parallax);
</code></pre><p>The code above is </p>
<ul>
<li>Getting the background element</li>
<li>Attaching a scroll event listener to the window</li>
<li>Every time the window is scrolled, the callback function gets the position the user has scrolled down vertically in pixels from <code>window.scrollY</code></li>
<li>It then moves the background image .4x the distance the window was scrolled by editing the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/top">CSS top property</a></li>
</ul>
<h2 id="heading-links">Links</h2>
<ul>
<li><a target="_blank" href="https://dev.to/javascriptacademy/create-parallax-scrolling-effect-with-vanilla-javascript-5b4h">Simple parallax example with instructions</a></li>
<li><a target="_blank" href="https://xd.adobe.com/ideas/principles/web-design/best-practices-for-parallax-websites/">Parallax scroll effect best practices with some incredible examples</a></li>
<li><a target="_blank" href="https://www.sbs.com.au/theboat/">The Boat - an amazing, immersive, storytelling experience for inspiration</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Understanding JavaScript reference versus instance]]></title><description><![CDATA[The Problem
Let's just say you're taking a break from spending 17 hours per day on Codewars on your holy quest to become an 8-Dan supreme master. 

Say you are trying to create a gameboard for a Battleship project. Say you are trying to do this with:...]]></description><link>https://blog.petefowler.dev/understanding-javascript-reference-versus-instance</link><guid isPermaLink="true">https://blog.petefowler.dev/understanding-javascript-reference-versus-instance</guid><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Tue, 09 Aug 2022 03:08:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1660013667760/Ox9McOOGc.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-problem">The Problem</h2>
<p>Let's just say you're taking a break from spending 17 hours per day on <a target="_blank" href="https://www.codewars.com/">Codewars</a> on your holy quest to become an 8-Dan supreme master. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660013193727/FBtTNqkWJ.jpg" alt="pexels-cottonbro-7792245.jpg" class="image--center mx-auto" />
Say you are trying to create a gameboard for a Battleship project. Say you are trying to do this with:</p>
<pre><code>const board <span class="hljs-operator">=</span> Array(<span class="hljs-number">10</span>).fill(Array(<span class="hljs-number">10</span>).fill(<span class="hljs-number">0</span>));
</code></pre><p><strong>Don't... just</strong> <strong><em>don't...</em></strong></p>
<p>What will happen here is you will seemingly have the board you wanted, an array of 10 filled with nested arrays of 10 zeros: </p>
<pre><code>[[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>], 
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>],
[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]]<span class="hljs-string">;</span>
</code></pre><p>You would then assume you can just change some of the values with:</p>
<pre><code>board[<span class="hljs-string">0</span>][<span class="hljs-symbol">0</span>] = 'battleship';
</code></pre><p>You would assume wrong. Every single one of the subarrays will now be set to 'battleship' at index 0 and this could be very difficult to debug without understanding what's going on. </p>
<h2 id="heading-but-why">But Why</h2>
<p>What happened here with <code>Array(10).fill(Array(10).fill(0))</code>? </p>
<p>Well, .fill is evaluated one time, so each "array" is not a separate array, but is <a target="_blank" href="https://stackoverflow.com/questions/41625393/changing-values-in-nested-js-arrays">actually the same thing - that one array instance</a>. They are all different <strong>references </strong>to the same array <strong>instance.</strong> <a target="_blank" href="https://stackoverflow.com/questions/35578478/array-prototype-fill-with-object-passes-reference-and-not-new-instance">See this also.</a></p>
<h2 id="heading-the-solution">The Solution</h2>
<p>Use a method that will actually create separate array instances for each element in board, perhaps: </p>
<pre><code>let board<span class="hljs-operator">=</span> Array(<span class="hljs-number">10</span>);
    <span class="hljs-keyword">for</span>(let i <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i <span class="hljs-operator">&lt;</span> board.<span class="hljs-built_in">length</span>; i <span class="hljs-operator">+</span><span class="hljs-operator">=</span> <span class="hljs-number">1</span>) {
      board[i] <span class="hljs-operator">=</span> Array(<span class="hljs-number">10</span>).fill(<span class="hljs-number">0</span>);
    }
</code></pre><h2 id="heading-a-similarly-counterintuitive-example">A Similarly Counterintuitive Example</h2>
<p>A similar situation could occur if you have a factory with a method that changes some variable, but you have only returned the reference to a variable, rather than another method/function that gets the current actual state of the variable. Consider this:</p>
<pre><code>const player <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  let health <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;
  const hit <span class="hljs-operator">=</span> (damage) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
    health <span class="hljs-operator">-</span><span class="hljs-operator">=</span> damage;
  }
  <span class="hljs-keyword">return</span> {health};
}
</code></pre><p>Counterintuitively, health does not represent the current value of the health property of the player object. It is a reference to the value (which was 100) right after it was created in the player object and subsequently returned. </p>
<p>If the player is attacked, the value of health should change. This could lead to an incorrect value of health always being 100, because it is pointing to the snapshot of the variable at the time it was created, rather than the current state of it on the player object.</p>
<h2 id="heading-another-solution">Another Solution</h2>
<p>To fix this, we would need a method that gets the actual current value of health:</p>
<pre><code>const player <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  let health <span class="hljs-operator">=</span> <span class="hljs-number">100</span>;
  const hit <span class="hljs-operator">=</span> (damage) <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> {
  health <span class="hljs-operator">-</span><span class="hljs-operator">=</span> damage;
  }
  const getHealth <span class="hljs-operator">=</span> () <span class="hljs-operator">=</span><span class="hljs-operator">&gt;</span> { 
    <span class="hljs-keyword">return</span> health; 
  }
  <span class="hljs-keyword">return</span> {getHealth};
}
</code></pre><h2 id="heading-reference-vs-instance">Reference vs Instance</h2>
<p>An instance of an object is an object that has been created and exists in memory. Reference to an object is something (like a variable) that points to an instance, allowing us to access it. <a target="_blank" href="https://stackoverflow.com/questions/6395754/difference-between-reference-and-instance-in-javascript#:~:text=Refrence%20is%20a%20variable%20that,variable%20%26%20methods%20that%20object%20have.&amp;text=Instance%20is%20the%20actual%20object%20created%20at%20run%20time.&amp;text=Just%20to%20be%20picky%3A%20Reference,a%20name%20%3A%20this%20is%20untrue.">See this link</a></p>
<p>In JavaScript, <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Glossary/Primitive">primitives</a> (string, number, bigint, boolean, undefined, symbol, and null) are passed by actual values. For example, <code>let name = 'Sal';</code> creates a space in memory to store the value 'Sal'. And <code>let person = 'Sal';</code> makes a different space in memory for the value 'Sal'. Changing the value of name won't affect person, since they are at different memory locations.</p>
<p>Arrays, objects, and functions do not operate the same way. Multiple variables can point to the same array, object, or function. For example, after <code>const people = ['hal', 'bob', 'sal'];</code> and <code>const persons = people;</code>, both people and persons are referring/pointing to the same array object at the one same location in memory. Changing either would change the array. 
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1660014391634/YPoLespwS.jpg" alt="pexels-cottonbro-7792284.jpg" class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Shout out to The Odin Project]]></title><description><![CDATA[Learn
The Odin Project (TOP) is a free, open source resource to learn web development. For those who can learn in a self-directed way, it provides a clear roadmap to gaining the necessary skills to launch a career in full stack web development. 
Cont...]]></description><link>https://blog.petefowler.dev/shout-out-to-the-odin-project</link><guid isPermaLink="true">https://blog.petefowler.dev/shout-out-to-the-odin-project</guid><category><![CDATA[Odin Project]]></category><category><![CDATA[learn coding]]></category><dc:creator><![CDATA[Pete Fowler]]></dc:creator><pubDate>Sat, 30 Jul 2022 21:55:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659736218823/s9K8TPGG6.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-learn">Learn</h3>
<p><a target="_blank" href="https://www.theodinproject.com/">The Odin Project</a> (TOP) is a free, open source resource to learn web development. For those who can learn in a self-directed way, it provides a clear roadmap to gaining the necessary skills to launch a career in full stack web development. </p>
<h3 id="heading-contribute">Contribute</h3>
<p>For people not new to development, there is a <em>great</em> opportunity to cement knowledge by helping others and answering questions on TOP's Discord channel, and to gain experience as a contributor to the project.
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659733391162/mcIUBIb6F.jpg" alt="odin.jpg" /></p>
<h3 id="heading-build-projects">Build projects</h3>
<p>The project-oriented style is engaging and practical, as ultimately what you are able to build and how you do it is the most important thing. Projects start very simple and progress through things like building a to do list, a Battleship game, and eventually things like a photo tagging app and an app replicating the core functionality of Facebook. There is also a section sharing wisdom on the job search process.</p>
<h3 id="heading-community">Community</h3>
<p>One of the greatest things about it is the TOP's community on Discord, where users can help each other or discuss searching for jobs. The success stories channel is the best, where seemingly every few days someone posts about getting their first developer job, thanking the TOP community, encouraging others, and talking about the value of grit and perseverance. Here is one <a target="_blank" href="https://www.theodinproject.com/success_stories">success story</a>:</p>
<blockquote>
<blockquote>
<p>An incredible self-paced curriculum that consists of the best resources for learning programming on the web! It was an invaluable resource on my path to a becoming a software developer. Thanks to The Odin Project I was able to get a job half way through the curriculum, and the projects completed as part of the curriculum gave me an edge compared to other junior developers with no experience.</p>
</blockquote>
</blockquote>
]]></content:encoded></item></channel></rss>