Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Unzipping Arrays and Excluding Items

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at how to implement array methods for excluding items and unzipping arrays.

unzip

unzip accepts an array of grouped elements and creates an array by regrouping the elements into their configuration before zipping them.

All we have to do to implement unzip is to loop through each of the arrays and rearrange them as follows:

const unzip = (zipped) => {
  let unzipped = zipped[0].map(z => []);
  for (let j = 0; j < unzipped.length; j++) {
    for (let i = 0; i < zipped.length; i++) {
      unzipped[j].push(zipped[i][j])
    }
  }
  return unzipped;
}

In the code above, we created a function that takes zipped arrays and inside the function, we mapped the first entry of zipped to a new array of arrays.

This is because the unzipped array should have arrays that are of the same size as each array entry in the zipped array.

Then we push the zipped entry in the given position into the unzipped array’s array entry.

For instance, if we call unzip with the following zipped array:

const result = unzip([
  ['a', 1, true],
  ['b', 2, false]
]);

Then unzip first maps the first zipped entry into a new array and set it to the unzipped variable.

Then the nested loop takes 'a' and then puts it into the first entry of the unzipped array. Then it pushes 'b' as the 2nd entry of the same array.

In the next iteration, it pushes 1 into the next unzipped array entry. Then it pushes 2.

The same process is done for the next iteration. Therefore, the unzipped array should be:

[
  [
    "a",
    "b"
  ],
  [
    1,
    2
  ],
  [
    true,
    false
  ]
]

unzipWith

unzipWith is like unzip , but we can pass in an iteratee function to choose how to regroup our array. The iteratee function takes 2 arguments and returns the one that’s the combination of the 2.

For instance, we can implement unzipWith as follows:

const unzipWith = (zipped, iteratee) => {
  let unzipped = zipped[0].map(z => []);
  for (let j = 0; j < unzipped.length; j++) {
    for (let i = 0; i < zipped.length; i++) {
      unzipped[j].push(zipped[i][j])
    }
  }
  return unzipped.map(arr => {
    return arr.reduce(iteratee);
  });
}

It’s almost the same as unzip except that we called reduce to combine the values with iteratee via the map method before returning the unzipped array.

Then when we call it as follows:

const result = unzipWith([
  [1, 10, 100],
  [2, 20, 200]
], (a, b) => a + b);

We get:

[
  3,
  30,
  300
]

for result .

without

The Lodashwithout method returns an array that excludes all the given values using the SameValueZero algorithm for comparison.

We can easily exclude items with our own without function as follows:

const without = (arr, values) => arr.filter(a => !values.includes(a))

In the code above, we called filter on arr and the leave the ones that aren’t in values by calling includes to check if it’s in values .

Then when we call it as follows:

const result = without([1, 2, 3, 4, 5], [4]);

We get that result is:

[
  1,
  2,
  3,
  5
]

xor

The xor function returns an array of unique values that aren’t included in all arrays. This is also known as the symmetric difference.

The order of the result is determined by when they occur in the array.

To implement it, we can just check whether they occur in all arrays and then exclude them if they do:

const xor = (...arrs) => {
  const symDiff = [];
  for (const arr of arrs) {
    for (const a of arr) {
      const inAllArrays = arrs.every(arr => arr.includes(a));
      if (!inAllArrays) {
        symDiff.push(a);
      }
    }
  }
  return symDiff;
}

In the code above, we looped through each array in arrs then inside, we loop through each array entry in each entry of arrs and check whether an element is in every array in arrs by using the every method on arrs .

The callback uses includes to check whether each element is in each array in arrs .

If it’s not, then we push it to the symDiff array, and in the end, we return that array.

Then when we call it as follows:

const result = xor([2, 1], [2, 3])

We get that result is:

[
  1,
  3
]

since 1 and 3 are not in all the arrays.

Conclusion

unzipping arrays means that we’re rearranging the entries so that each entry in the nested array is now in their own array position.

We can also return a new array with some entries excluded with the without function, which we can implement with filter and includes .

Finally, we can find the symmetric difference by checking if the elements are included in each array in the nested array and exclude those.

Categories
Lodash

Learning JavaScript by Implementing Lodash Methods — Objects and Functions

Lodash is a very useful utility library that lets us work with objects and arrays easily.

However, now that the JavaScript standard library is catching up to libraries such as Lodash, we can implement many of the functions in simple ways.

In this article, we’ll look at some Lodash object and function methods that we can replace with our own plain JavaScript implementation.

unary

The Lodash unary method returns a function that only calls the original function with one argument.

We can just create our own function that returns a function that calls the original function ourselves as follows:

const unary = (fn) => (val) => fn(val);

In the code above, we have our own unary function, which takes a function that returns (val) => fn(val) , which is a function that only takes one argument and then calls fn with that argument.

Then we can call it as follows:

const result = ['6', '8', '10'].map(unary(parseInt));

And we get:

[
  6,
  8,
  10
]

as the value of result .

wrap

The Lodash wrap method takes a wrapper function another function fn that we want to call with the wrapper as the first argument.

It returns a function that takes in a value val as the parameter and calls the function in the second argument with the wrapper as the first argument and val as the 2nd argument.

For instance, we can implement our own wrap function as follows:

const wrap = (wrapper, fn) => (val) => fn(wrapper, val)

In the code above, the wrap function takes wrapper and fn , which are functions. Then we return a function that takes in an argument, which is val and then calls fn with wrapper and val .

Then we can call it as follows with the Lodash _.escape method as the first argument and our own function to wrap our text around p tags as follows:

const p = wrap(_.escape, (func, text) => `<p>${func(text)}</p>`);

In the code above, the escape method escapes the text and our own function wraps func(text) , where func is the escape method since that’s the first argument and text is the text that we’ll pass into the returned p function.

Then we can call p as follows:

const result = p('foo bar');

and we get that result is <p>foo bar</p> .

forIn

The Lodash forIn method loops through the own and inherited enumerable string keyed properties of an object and run an iteratee function on each property.

The iteratee is a function that takes the value , key , and object parameters and we can stop the loop by returning false explicitly

We can implement our own forIn function as follows:

const forIn = (object, iteratee) => {
  for (const key in object) {
    const result = iteratee(object[key], key, object);
    if (result === false) {
      break;
    }
  }
}

In the code above, we have the forIn function that takes an object and an iteratee function.

Then inside the function, we used the for...in loop to loop through all the enumerable own and enumerable string keys of object .

Inside the loop, we call the iteratee function with the value, which we get by using object[key] , the key itself, and the object . We assign the return value of iteratee to result .

Then if result is false , then we break the loop. Otherwise, it continues running.

We can then call our forIn function as follows:

function Foo() {
  this.a = 1;
  this.b = 2;
}

Foo.prototype.c = 3;

forIn(new Foo(), (value, key) => console.log(key));

In the code above, we have the Foo constructor, which has 2 own properties a and b . It also has an additional inherited property c .

Then we called forIn with a Foo instance, and pass in a callback that logs the keys of the Foo instance.

In the end, we should see a , b and c logged in the console log output.

Conclusion

The unary and wrap Lodash methods both return a function that takes in some arguments and call the original function that’s passed in as the argument from the returned function.

Lodash’s forIn method is like the for...in loop, except that we can return false with our iteratee function to break the loop. The iteratee is run on every iteration until it returns false .

Categories
JavaScript

Making Cancel-able HTTP Requests with JavaScript Fetch API

The Fetch API is a big step forward from the old XMLHttpRequest object for making HTTP requests. Now it’s even better with the addition of the AbortController , which lets us make cancelable HTTP requests, which isn’t possible before.

In this article, we’ll look at how to combine the Fetch API with the new AbortController to create cancelable HTTP requests.

**AbortController and Fetch**

We can use the AbortController to create cancellable HTTP requests. To do this, we create a new AbortController using the AbortController.AbortController() constructor. Then communication with the DOM is done using the AbortSignal object.

Then using the AbortController.signal property we can get the reference to the AbortSignal object, which can be passed into the fetch function to abort an HTTP request.

For instance, we can make a GET request to download a video that can be called by writing the following code. First we create buttons for initiating the request and canceling the request when clicked as follows:

<button class='download'>
  Download
</button>
<button class='abort'>
  Abort
</button>

Then we can make our cancelable request as follows:

const controller = new AbortController();
const signal = controller.signal;
const downloadBtn = document.querySelector('.download');
const abortBtn = document.querySelector('.abort');

const fetchVideo = async () => {
  try {
    const url = 'https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4';
    const response = await fetch(url, {
      signal
    });
  } catch (e) {
    console.log(e.message);
  }
}

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', function() {
  controller.abort();
  console.log('Download aborted');
});

In the code above, we have the first 2 lines:

const controller = new AbortController();
const signal = controller.signal;

which creates the AbortController object and then get the AbortSignal object assign it to the signal constant. The AbortSignal sends the cancelation request.

Then in the fetchVideo function, we pass in the signal into the object that’s in the 2nd argument of fetch .

Next, we hook up the buttons to our click handler for each button by writing:

downloadBtn.addEventListener('click', fetchVideo);

abortBtn.addEventListener('click', () => {
  controller.abort();
  console.log('Download aborted');
});

Now when we click the Download button and then click Abort before the download is done, we’ll see Download aborted and `The user aborted a request’ show in the console log. In the Network tab of Chrome’s developer console, we’ll see that our request’s status is canceled.

Compatibility

AbortController is still considered an experimental object. However, it’s available in the latest Chromium browsers like Chrome. It’s also available on Edge, Safari, and Firefox.

It’s not available in Internet Explorer so we can use it there.

Conclusion

We can use the AbortController object and the associated AbortSignal with the Fetch API to make cancelable HTTP requests. Once the AbortSignal is sent, the HTTP request is canceled and won’t be sent if the cancellation signal is sent before the HTTP request is done.

Categories
Vuetify

Vuetify — Temporary Drawer

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Temporary Drawer

We can create a temporary drawer that sits above the app and use an overlay to darken the background.

Clicking outside the drawer will cause it to close.

For example, we can write:

<template>
  <v-row>
    <v-col cols="12">
      <v-sheet height="400" class="overflow-hidden" style="position: relative;">
        <v-container class="fill-height">
          <v-row align="center" justify="center">
            <v-btn color="pink" dark @click.stop="drawer = !drawer">Toggle</v-btn>
          </v-row>
        </v-container>

        <v-navigation-drawer v-model="drawer" absolute temporary>
          <v-list-item>
            <v-list-item-avatar>
              <v-img src="https://randomuser.me/api/portraits/men/78.jpg"></v-img>
            </v-list-item-avatar>

            <v-list-item-content>
              <v-list-item-title>John Smith</v-list-item-title>
            </v-list-item-content>
          </v-list-item>

          <v-divider></v-divider>

          <v-list dense>
            <v-list-item v-for="item in items" :key="item.title" link>
              <v-list-item-icon>
                <v-icon>{{ item.icon }}</v-icon>
              </v-list-item-icon>

              <v-list-item-content>
                <v-list-item-title>{{ item.title }}</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
        </v-navigation-drawer>
      </v-sheet>
    </v-col>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    drawer: null,
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

We have the v-navigation-drawer component to add the drawer that opens when we click on the Toggle button.

The Toggle button is created with a v-btn component with the click listener.

We just toggle the drawer variable between true and false to toggle the menu.

The drawer state is used by the v-navigation-drawer ‘s v-model directive to control the opening and closing of the drawer.

The drawer items are in the v-list-item components.

Right Positioned Menu

We can position the menu on the right side of the screen.

For example, we can write:

<template>
  <v-row>
    <v-col cols="12">
      <v-card height="350px">
        <v-navigation-drawer absolute permanent right>
          <template v-slot:prepend>
            <v-list-item two-line>
              <v-list-item-avatar>
                <img src="https://randomuser.me/api/portraits/women/81.jpg" />
              </v-list-item-avatar>

<v-list-item-content>
                <v-list-item-title>Jane Smith</v-list-item-title>
                <v-list-item-subtitle>Logged In</v-list-item-subtitle>
              </v-list-item-content>
            </v-list-item>
          </template>

<v-divider></v-divider>

<v-list dense>
            <v-list-item v-for="item in items" :key="item.title">
              <v-list-item-icon>
                <v-icon>{{ item.icon }}</v-icon>
              </v-list-item-icon>

              <v-list-item-content>
                <v-list-item-title>{{ item.title }}</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </v-list>
        </v-navigation-drawer>
      </v-card>
    </v-col>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    drawer: null,
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

to place it on the right with the right prop.

Conclusion

We add the temporary drawer and place it on the left or right side of the screen with Vuetify.

Categories
Vuetify

Vuetify — Menu Transitions and Positions

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Menu Custom Transitions

Custom transitions can be added to menus.

Vuetify comes with 3 standard transitions, which are scale , slide-x and slide-y .

For example, we can write:

<template>
  <v-row justify="space-around">
    <v-menu bottom origin="center center" transition="scale-transition">
      <template v-slot:activator="{ on, attrs }">
        <v-btn color="primary" dark v-bind="attrs" v-on="on">Scale Transition</v-btn>
      </template>

      <v-list>
        <v-list-item v-for="(item, i) in items" :key="i">
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

We have the transition prop to set the transition effect.

The on object is passed in as the value of v-on .

This lets us listen to events for the menu.

And attrs are props that we passed into v-bind so that we can set the props to show the menu.

The other choices for the transition prop are slide-x-transition and slide-y-transition .

Disabled Menu

We can disable the menu with the disabled prop.

For example, we can write:

<template>
  <v-row justify="space-around">
    <v-menu bottom origin="center center" transition="scale-transition" disabled>
      <template v-slot:activator="{ on, attrs }">
        <v-btn color="primary" dark v-bind="attrs" v-on="on">Menu</v-btn>
      </template>

<v-list>
        <v-list-item v-for="(item, i) in items" :key="i">
          <v-list-item-title>{{ item.title }}</v-list-item-title>
        </v-list-item>
      </v-list>
    </v-menu>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

then the button won’t open the menu.

X Offset

We can move the menu horizontally to make the activator visible.

For example, we can write:

<template>
  <v-row justify="space-around">
    <v-col cols="12">
      <v-menu bottom origin="center center" transition="scale-transition" offset-x>
        <template v-slot:activator="{ on, attrs }">
          <v-btn color="primary" dark v-bind="attrs" v-on="on">Menu</v-btn>
        </template>

        <v-list>
          <v-list-item v-for="(item, i) in items" :key="i">
            <v-list-item-title>{{ item.title }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-menu>
    </v-col>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

We move the menu to the right of the button with the offset-x prop.

Y Offset

Similarly, we can move the menu vertically.

For example, we can write:

<template>
  <v-row justify="space-around">
    <v-col cols="12">
      <v-menu bottom origin="center center" transition="scale-transition" offset-y>
        <template v-slot:activator="{ on, attrs }">
          <v-btn color="primary" dark v-bind="attrs" v-on="on">Menu</v-btn>
        </template>

        <v-list>
          <v-list-item v-for="(item, i) in items" :key="i">
            <v-list-item-title>{{ item.title }}</v-list-item-title>
          </v-list-item>
        </v-list>
      </v-menu>
    </v-col>
  </v-row>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: [
      { title: "Click Me" },
      { title: "Click Me 2" },
      { title: "Click Me 3" },
    ],
  }),
};
</script>

to move the menu above the menu button.

Conclusion

We can add menus with different transitions and positions.