Array Methods Make Life Worth Living

Cover Image for Array Methods Make Life Worth Living

Proponents for positivity and mindfulness practitioners suggest expressions of gratitude contribute to one's overall sense of well being. When reflecting on gratitude sources in our daily life, it is easy to jump to "family," "skilled baristas," or "cool dogs." Like many, I too am grateful for compassionate parents, good lattes, and hanging out with friendly puppies.

Delving deeper into this introspective exercise, I shifted my focus onto things I experience in my day-to-day that make life worth living - the lovely trivialities of life. For what am I truly grateful? JavaScript array and object constructor methods!

There is little in life I enjoy more than finding seemingly novel ways to assemble JavaScript's array methods for succinct data transformation. In this "Ode to JavaScript's Array Methods," I'll detail a few neat things I've thrown together over the past development week.

De-dupe JavaScript Arrays

For one reason or another, you may find yourself desiring an array of unique values. JavaScript's Array.from() method and Set object are the tools for the task,

const fruits = ["🍎", "🍎", "🥝", "🥝", "🍊", "🍊"];

const uniqFruits = Array.from(new Set(fruits));

console.log(uniqFruits); // [ "🍎", "🥝", "🍊" ]

Above we have array fruits, which has duplicate values for each fruit in the array. Because each value in a Set instance may only occur once, we can use it to remove duplicate values from the fruits array. We then call Array.from with the Set instance to create an array with the duplicate fruit string values removed.

Reduce an array of arrays into a single array of objects

Sticking with the theme of fruits, let's create a single array from an array of arrays.

const fruits = [["🍎"], ["🍎"], ["🥝"], ["🥝"], ["🍊"], ["🍊"]];

const newFruits = fruits.reduce((acc, fruit) => [...acc, ...fruit]);

console.log(newFruits); // [ '🍎', '🍎', '🥝', '🥝', '🍊', '🍊' ]

And with the duplicate fruit string values removed,

const fruits = [["🍎"], ["🍎"], ["🥝"], ["🥝"], ["🍊"], ["🍊"]];

const uniqFruits = Array.from(
  new Set(fruits.reduce((acc, fruit) => [...acc, ...fruit]))
);

console.log(uniqFruits); // [ "🍎", "🥝", "🍊" ]

Sequential Asynchronous Requests with Async, Await, and Reduce

Breaking from our fruit theme, the final example demonstrates how to use async/await and the .reduce() array method to sequentially resolve asynchronous requests. I was recently geocoding several thousand addresses. According to our friends at Wikipedia, Geocoding is,

The computational process of transforming a physical address description to a location on the Earth's surface.

To achieve this, I leveraged Google's Geocode API to get the longitude and latitude of businesses based upon their street addresses. Dealing with several thousand address records, it seemed more manageable to sequentially request the lat/long for each address. As you'll see in the example below, we can leverage async/await and the .reduce() array method to handle the sequencing of asynchronous requests.

const googleGeocodeApi = `https://maps.googleapis.com/maps/api/geocode/json`
const apiKey = '__YOUR_API_KEY__'

const hotels = [
  {
    name: 'The Lodge at Whitefish Lake',
    street: '1380 Wisconsin Ave',
    city: 'Whitefish',
    state: 'MT',
    zip: 59937
  },
  {
    name: 'Hotel Jackson',
    street: '120 Glenwood St',
    city: 'Jackson',
    state: 'WY',
    zip: 83001
  }
]

async function getHotelCoords(hotel) {
  const { name, street, city, state, zip } = hotel

  const join = (attr) => attr.split(' ').join('+')
  const query = `?address=${ join(name) },${ join(street) },${ city },${ state } ${ zip }`

  const uri = `${ googleGeocodeApi }${ query }&key=${ apiKey }`

  const response = await fetch(uri)
  const { results: [ result ] } = await response.json()

  const {
    geometry: {
      location: {
        lat: latitude,
        lng: longitude
      }
    }
  } = result

  return { latitude, longitude }
}

async function geocodeHotels()
  const geocodedHotels = await hotels.reduce(async (prevPromise, hotel) => {
    const acc = await prevPromise

    const coords = await getHotelCoords(hotel)

    return Promise.resolve([ ...acc, Object.assign({}, hotel, coords)])
  }, Promise.resolve([]))

  console.log(geocodedHotels)
}

geocodeHotels()

To achieve this, we reduce an array of hotel objects, fetch their latitude and longitude from the Google Geocode API, and assign the values to a new hotel object. The result is an array of hotels with data akin to this,

[
  {
    "name": "The Lodge at Whitefish Lake",
    "street": "1380 Wisconsin Ave",
    "city": "Whitefish",
    "state": "MT",
    "zip": 59937,
    "latitude": 48.431149,
    "longitude": -114.342146
  }
  // ...
]

Wrapping Up

These are but a few examples illustrating the wonderful ways in which JavaScript array and object constructor methods contribute to the joys of being a developer. My appreciation for JavaScript's array methods is undoubtedly attributed to life prior to .map(), .filter(), and .reduce(). Life is more joyous with fewer for loops.There are many, many more - we didn't even get a chance to explore some of the radical data transformations possible by chaining array methods! But as it's a sunny Sunday afternoon, I'll save those for another day 🏊‍♂️