<?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" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Amgine]]></title><description><![CDATA[Amgine]]></description><link>https://amgines.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!dRYU!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12bf003d-1a0f-43a4-bd92-db7e3abf6130_259x259.jpeg</url><title>Amgine</title><link>https://amgines.substack.com</link></image><generator>Substack</generator><lastBuildDate>Thu, 04 Jun 2026 06:32:10 GMT</lastBuildDate><atom:link href="https://amgines.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Amgine]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[amgines@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[amgines@substack.com]]></itunes:email><itunes:name><![CDATA[AMGx]]></itunes:name></itunes:owner><itunes:author><![CDATA[AMGx]]></itunes:author><googleplay:owner><![CDATA[amgines@substack.com]]></googleplay:owner><googleplay:email><![CDATA[amgines@substack.com]]></googleplay:email><googleplay:author><![CDATA[AMGx]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[From Text to Binary and Back]]></title><description><![CDATA[Understanding data beyond JSON in JS]]></description><link>https://amgines.substack.com/p/from-text-to-binary-and-back</link><guid isPermaLink="false">https://amgines.substack.com/p/from-text-to-binary-and-back</guid><dc:creator><![CDATA[AMGx]]></dc:creator><pubDate>Thu, 26 Jun 2025 02:39:53 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!hA-P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Throughout my career I have seen these APIs and data structures in many different use cases without really understanding what they were doing. I have worked periodically with pdf creation and pdf content extraction and I had to always use some strange data structures to get what I needed. Very recently I had to work with these mysterious APIs and structures again and I realized that this was something that was going to hunt me for the rest of my career. So I decided to finally roll up my sleeves and understand it better. </p><p></p><h3>Everything starts with binary</h3><p>As you probably already know computers store everything (text, images, videos, etc) in binary. That is, they store everything with 1s and 0s. That is the computers&#8217; language. When we talk about working with raw data we are saying we are working with binary data. </p><pre><code>hello -&gt; Text (human language)

01001000 01100101 01101100 01101100 01101111 -&gt; hello in binary</code></pre><p>JS uses a raw binary container called <strong>ArrayBuffer </strong>to represent binary. Since JS is a high-level language, it does not allow you to access the bytes of an ArrayBuffer without using special tools. To be able to read and update binary data in JS we need <em>Typed Arrays.</em></p><p></p><h3>Working with Typed Arrays</h3><p>Typed arrays are the tool we need to be able to read and write data from and to an ArrayBuffer. Depending on your need you would use a specific typed array. Generally speaking you have signed integer typed arrays, which cover ranges from negative to positive values (e.g Int8Array) and unsigned integer typed arrays, which only cover positive ranges (e.g Uint8Array). You also depend on the size of data you are working with. You can have 8, 16 and 32 bit signed and unsigned integer arrays and 32 and 64 bit float arrays.  </p><pre><code>const binary = new ArrayBuffer(4);
console.log(binary) // []

const uint8Array = new Uint8Array(binary);
console.log(uint8Array); //[0,0,0,0] - Initial binary data

// Now let's try to modify ArrayBuffer directly
binary[0] = 72;
console.log(uint8Array); //[0,0,0,0]

// Now modify using Uint8Array
uint8Array[0] = 72;
console.log(uint8Array); //[72,0,0,0]</code></pre><p>The example above shows how you cannot read or update binary in JS without using typed arrays. Think of typed arrays as special lenses you need to be able to see a particular set of data. In our previous example the constant binary would have a structure like this:</p><pre><code>[ 00000000 ][ 00000000 ][ 00000000 ][ 00000000 ]  // 4 bytes</code></pre><p>If we use a &#8220;UInt8Array&#8221; lens what we are saying is: &#8220;Group those bits in groups of 8 and read each group as an integer from 0 to 255 (the range of a UInt8Array)&#8221;. So what you would end up having is something like:</p><pre><code>Uint8Array { 0: 0, 1: 0, 2: 0, 3: 0 } </code></pre><p>If you were to use a Uint16Array instead you would group those bits in groups of 16 like this:</p><pre><code>[ 00000000 00000000 ][ 00000000 00000000 ]</code></pre><p>And you would get something like:</p><pre><code>Uint16Array { 0: 0, 1: 0 } </code></pre><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h3>How Text Gets In</h3><p>Now that we have covered the basics it is time to understand the flow of how text gets converted to bytes and back. JS provides us with an interface called TextEncoder to be able to translate human readable text into bytes. The TextEncoder does this by implementing its <em>encode</em> method. This method takes a string as its only parameter and returns a Uint8Array of UTF-8 encoded text. </p><p>If you get confused about what UTF-8 is and how does it relate to typed arrays you are not alone. A typed array tells you how to read the raw bytes in memory (in this case, as 8-bit unsigned integers (0&#8211;255)). That works perfectly for UTF-8 encoded text because UTF-8 also operates at the byte level. </p><p>UTF-8 itself is a text encoding format and what it tells you is how to represent characters (like H, &#233;, or <code>&#128522;</code>) as a series of one or more bytes. In our earlier example, each number in the Uint8Array represents one byte of UTF-8 data. So UTF-8 is the map, and the array of bytes is the route you follow to reconstruct the original string.</p><p>You have probably heard of other encodings like UTF-16 or ASCII. These are just alternative maps for translating characters into bytes.</p><p>Since the TextEncoder returns a Uint8Array we know that it is basically representing an ArrayBuffer and giving us a way of reading its content and of updating it. </p><pre><code>const myString = "Hello";

const textEncoder = new TextEncoder();
const utf8Representation = textEncoder.encode(myString);

console.log(utf8Representation);
//{ 0: 72, 1: 101, 2: 108, 3: 108, 4: 111 }
// Where 72 is H, 101 is e, 108 is l and 111 is o in UTF-8 format</code></pre><p>Now, that is something our computer can understand!</p><p></p><h3>How Text Gets Out</h3><p>Similarly, JS provides us with a decoding interface called TextDecoder. As you could have guessed, the TextDecoder does exactly the opposite as the TextEncoder. It uses its <em>decode</em> method to get a string (in human readable form) from a stream of bytes, or ArrayBuffer with any representation (e.g. UTF-8).</p><pre><code>const utf8Decoder = new TextDecoder('utf-8');

const uint8Array = new Uint8Array([72, 101, 108, 108, 111])
const decodedString = utf8Decoder.decode(uint8Array);

console.log(decodedString); //"Hello"</code></pre><p>Even though this seems trivial, you need to know the encoding format (UTF-8, UTF-16, etc) and the byte structure (Uint8Array, Uint16Array, etc) to be able to decode the bytes properly, though in most practical cases, UTF-8 and Uint8Array are the default and safest combination. I invite you to try the above code just changing the utf-8 for utf-16 or Uint8Array for Uint16Array. You will often get incorrect output (or decoding errors, depending on the format mismatch).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h3>A supermarket analogy</h3><p>Let&#8217;s pause here for a moment and try to digest all these new concepts first, before we move on. Let&#8217;s look at this process as something similar to what you see in a supermarket:</p><ul><li><p>Some cookies would be the <em>raw binary data. </em></p></li><li><p>The package that contains the cookies would be the <em>ArrayBuffers </em>(You can&#8217;t see the cookies)<em>.</em></p></li><li><p>The barcode in the package would be the Uint8Array (which gives you a numerical way to identify what is inside the package)</p></li><li><p>The scanner at the cashier would be the TextDecoder</p></li><li><p>The software that translate the barcode to human readable content would be UTF-8</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!hA-P!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!hA-P!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!hA-P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png" width="728" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;normal&quot;,&quot;height&quot;:1024,&quot;width&quot;:1024,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:1796947,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://amgines.substack.com/i/165837648?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!hA-P!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 424w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 848w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 1272w, https://substackcdn.com/image/fetch/$s_!hA-P!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe44498a5-03b1-40dd-98f6-67a15568f6c8_1024x1024.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Not everything is text</h3><p>At this point, we understand how to convert text into binary and vice versa. But what if what we are dealing with is not text? What if it is an image, a pdf or an audio? You would probably get pure nonesense if you try to decode the binary with the TextDecoder. So what should we use if we are handling something other than text? This is where the Blob comes in. </p><p>A Blob (short for Binary Large OBject) represents binary data as an immutable file-like object. As a matter of fact, the File class extends from the Blob and adds some more metadata. While an <em>ArrayBuffer </em>helps you have some low-level control over binary content, a Blob&#8217;s job is to wrap and transfer binary data. A Blob is ideal to send, preview or download binary content.</p><pre><code>const imageBlob = new Blob([arrayBuffer], { type: 'image/png' });
const url = URL.createObjectURL(imageBlob);
document.querySelector('img').src = url;</code></pre><p>This takes binary data (e.g., fetched from a server) and wraps it in a Blob.</p><p></p><h3>Moving and storing binary in text-only systems</h3><p>Sometimes you&#8217;re working in systems that only accept plain text. In those cases, you might not be able to just use an <em>ArrayBuffer</em> or <em>Blob</em>. In cases like these you might want to convert your binary content to <em>Base64</em>. </p><p>Base64 is a way of encoding binary data as plain text using only ASCII characters. It ensures your binary data transfers safely without corruption. It is worth noting that when converting to Base64 your data becomes around 33% bigger. Therefore, make sure you are using it for compatibility purposes and not for performance.</p><p>Some use cases for converting binary to Base64 are:</p><ul><li><p>Embedding images or files in JSON</p></li><li><p>Sending attachments in emails (MIME format)</p></li><li><p>Embedding an image into HTML directly instead of pointing to an image URI</p></li></ul><p>Newer APIs like Uint8Array.prototype.toBase64() are part of an upcoming standard and are only supported in some browsers like Safari and Firefox. In most environments, you&#8217;ll still need to use <em>btoa()</em> (for encoding) and <em>atob()</em> (for decoding). These are older, browser-only methods with limitations (for example, they don&#8217;t work in Node.js and only handle ASCII-safe strings).</p><pre><code>const someImageBinary = new Uint8Array([0x89, 0x50, 0x4E, 0x47, ...]);

function uint8ToBase64(uint8) {
  let binary = '';
  for (let i = 0; i &lt; uint8.length; i++) {
    binary += String.fromCharCode(uint8[i]);
  }
  return btoa(binary);
}

const imageInBase64 = uint8ToBase64(someImageBinary);

const dataURI = `data:image/png;base64,${imageInBase64}`;

console.log(dataURI);
//'data:image/png;base64,iVBORw0K...'</code></pre><h5>Note: If you need to embed a base64 string you might need to create a Data URI manually like in the example</h5><p></p><h3>Conclusion</h3><p>I spent years ignoring these tools because I didn&#8217;t know what they were really doing. Now that I understand them, I see how powerful they are and how often they show up in places I never expected. If you are working with binary, text, files or just weird browser APIs, understanding this layer can save you hours (or even days).</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Amgine! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item><item><title><![CDATA[Breaking forEach]]></title><description><![CDATA[Understanding the limitations of the forEach method]]></description><link>https://amgines.substack.com/p/breaking-foreach</link><guid isPermaLink="false">https://amgines.substack.com/p/breaking-foreach</guid><dc:creator><![CDATA[AMGx]]></dc:creator><pubDate>Wed, 30 Apr 2025 04:56:37 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!dRYU!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F12bf003d-1a0f-43a4-bd92-db7e3abf6130_259x259.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>One of the first array methods that one learns in JS is the forEach method. After all, it is a simple way of running some logic on each item in a list. But as we move on and start to create more complex code, we might get some inconsistencies in the behavior of our app or we might not realize that there is potential unpredictable functionality in our features. Let&#8217;s dive a little deeper into forEach and understand its limitations.<br><br>Before diving into limitations, let&#8217;s break forEach down. The forEach method has one mandatory parameter and one optional parameter. The mandatory parameter is a callback function and the optional is the <em>this</em> argument, which I will cover in another post. The callback function takes 3 parameters itself: the current element, the index and the array the <em>forEach</em> was called upon. This method does not return any value. The following example can give us a glimpse of how it works:</p><pre><code>const singular = ["apple", "orange", "banana"]

const returnedValue = singular.forEach((value, index, array) =&gt; {
  console.log(value, index, array);
  return value;
})

console.log({returnedValue})

// apple 0 ["apple", "orange", "banana"]
// orange 1 ["apple", "orange", "banana"]
// banana 2 ["apple", "orange", "banana"]

// {returnedValue: undefined}</code></pre><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://amgines.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p><h4>Limitation 1: Not chainable</h4><p>By now, the first limitation should be apparent. We cannot have a forEach in the middle of a method chain, only at the end.</p><pre><code>const singular = ["apple", "orange", "banana"]
const plural = []

singular.map((value) =&gt; `${value}s`).forEach((value) =&gt; {
  plural.push(value)
})

console.log(plural)

//["apples", "oranges", "bananas"]</code></pre><pre><code>const singular = ["apple", "orange", "banana"]

const chained = singular.forEach((value) =&gt; value).map((value) =&gt; `${value}s`)

console.log(chained)

// Uncaught TypeError: Cannot read properties of undefined (reading 'map')"</code></pre><h4>Limitation 2: Not asynchronous</h4><p>Unlike other methods like <em>map, </em>forEach does not support asynchronous callbacks. This limitation is often overlooked and is particularly dangerous because it is silent. It won&#8217;t throw an error if you pass it an asynchronous callback, it will just run but with unpredictable outcomes in your code.</p><pre><code>const singular = ["apple", "orange", "banana"]
const getIndex = (index) =&gt; new Promise((resolve) =&gt; setTimeout(() =&gt; resolve(index), Math.random() * 1000));

singular.forEach(async (item, index) =&gt; {
    const i = await getIndex(index);
    console.log(i);
});

// 1
// 2
// 0</code></pre><h4>Limitation 3: Unpredictable with sparse arrays</h4><p>The forEach method invokes the callback function only for indexes in the array that have explicitly assigned values (including null and undefined).</p><pre><code>const cinemaClients = ["George", "John", "Pete", "Matt"]
const sparseReservedSeats = [1, 2, , 4]
const reservedSeats = [1, 2, undefined, 4]

function getSeatsByUser (array){
    const seatsByUser = {}

    array.forEach((value, index) =&gt; {
        const client = cinemaClients[index]
        if(value === undefined){
            seatsByUser[client] = 0
        }else{
            seatsByUser[client] = value
        }
    })

    return seatsByUser
}

console.log(getSeatsByUser(sparseReservedSeats))
// { George: 1, John: 2, Matt: 4 }

console.log(getSeatsByUser(reservedSeats))
// { George: 1, John: 2, Pete: 0, Matt: 4 }</code></pre><h4>Limitation 4: Only works with array-like objects</h4><p>forEach is a <em>generic</em> method. This means that it can be applied to any object that has integer keys and a length property of type integer. But you may think &#8220;I have seen the forEach method being applied on Sets and Maps and they don&#8217;t have integer keys or a length property&#8221;. You are partly right. We can apply forEach to Maps and Sets, but those forEach methods are not borrowed from the Array prototype. Sets and Maps have their own built-in forEach methods that behave differently.</p><pre><code>const arrayLikeObj = {
    length: 3,
    0: "Hello, ",
    1: "my friend, ",
    2: "hello."
}

const obj = {
    0: "Goodbye, ",
    1: "my friend, ",
    2: "goodbye."
}

function getGreeting(obj){
    let greeting = ""
    Array.prototype.forEach.call(obj, ((value) =&gt; {
        greeting += value
    }))
    return greeting
}

console.log(getGreeting(arrayLikeObj))
// 'Hello, my friend, hello.'

console.log(getGreeting(obj))
// ''</code></pre><h4>Limitation 5: No way to stop it</h4><p>Unlike <em>for loops</em> that can skip iterations or stop loops and other methods like every, some, find and findIndex that stop the loop once they have met a certain condition, the forEach method does not have a natural way of stopping loops. The only way to stop the loop is by throwing an error.</p><p></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://amgines.substack.com/subscribe?"><span>Subscribe now</span></a></p><p></p><h3>Looking under the hood</h3><p>Now that we understand how the <em>forEach</em> method works and have seen its limitations, let&#8217;s take one final look under the hood to see why these limitations exist in the first place. We could write our own simplified version of forEach like this:</p><pre><code>Array.prototype.myForEach = function(callback, thisArg) {
    if (typeof callback !== 'function') {
        throw new TypeError('Callback is not a function');
    }

    // Bind 'thisArg' if it's provided
    if (thisArg) {
        callback = callback.bind(thisArg);
    }

    const obj = Object(this);
<strong>    // Limitation 4: We need to have a length property</strong>
    const objLength = obj.length;

    for (let i = 0; i &lt; objLength; i++) {
        <strong>// Limitation 3: We only work with explicitly assigned values</strong>
        if (obj.hasOwnProperty(i)) {
            callback(obj[i], i, obj);
        }
    }

    <strong>// Limitation 1: Always return undefined</strong>
    return undefined;
}</code></pre><p></p><h3>Conclusion</h3><p>While you can solve most problems using different types of loops, choosing the right one is about more than just personal preference, it&#8217;s about using the right tool for the job. </p><p>You should probably avoid forEach if:</p><ul><li><p>you need the loop to return a value</p></li><li><p>you need to work with asynchronous operations</p></li><li><p>you are iterating over sparse arrays</p></li><li><p>you are dealing with objects that lack a length property or don't use integer keys</p></li><li><p>you need a way to break early or exit the loop</p></li></ul><p>Hopefully, by understanding how forEach works behind the scenes, you can feel more confident and intentional about which loop you choose in the future.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://amgines.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading Amgine! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item></channel></rss>