Day 19: Transforming API Data
Fetch a dataset where all items exist with numeric values, transform the data into alphabetically grouped sections, and render all items while only enabling links for those with values greater than zero.
JavaScript focus
- transforming object → grouped array
- conditional rendering based on values
- deciding element type (anchor vs span)
- maintaining data integrity (don't mutate original unnecessarily)
- building UI from structured data
Nice extras
- add a class like .is-disabled for items with 0
- show counts next to items (optional)
- generate encoded/slugs for links (only for clickable items)
- avoid creating anchor tags at all for disabled items (cleaner than disabling them)
- separate “data transformation” from “render logic”
MDN prep
Transforming API Data
The HTML
<!-- use createElement to render the UI -->
<div class="columns_wrap">
<article class="article">
<h3 class="tertiary_heading">A</h3>
<ul class="list_items">
<li><a href="/affenpinscher">Affenpinscher</a></li>
<li>Afghan Hound</li>
<li><a href="/airedale-terrier">Airedale Terrier</a></li>
<li><a href="/akita">Akita</a></li>
<li>Alaskan Malamute</li>
<li><a href="/american-bulldog">American Bulldog</a></li>
<li><a href="/australian-shepherd">Australian Shepherd</a></li>
</ul>
</article>
</div>
The JavaScript
async function fetchDogBreeds() {
try {
const response = await fetch("../data/dog-breeds.json");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("There was a problem: ", error);
}
}
function initTransformData(data) {
const breedKeysAndValues = Object.entries(data);
const breedKeys = Object.keys(data);
const firstLetter = breedKeys.map((letter) => letter[0]);
const heading = firstLetter.filter((first, index) => firstLetter.indexOf(first) === index);
heading.sort();
const newBreedsArray = [];
heading.forEach((letter) => {
const newListObject = {
heading: letter,
list: [],
};
breedKeysAndValues.forEach((items) => {
const keyLetter = items[0].charAt(0);
const link = items[0].replaceAll(" ", "-").toLowerCase();
const newBreedObject = {
breed: items[0],
count: items[1],
url: link,
};
if (keyLetter === letter) {
newListObject.list.push(newBreedObject);
}
});
newBreedsArray.push(newListObject);
});
console.log(newBreedsArray);
return newBreedsArray;
}
function initRenderUI(data) {
const root = document.querySelector(".transformed_data");
if (!root) return;
const columns = document.createElement("div");
columns.classList.add("columns_wrap");
data.forEach((items) => {
const article = document.createElement("article");
article.classList.add("article");
const h3 = document.createElement("h3");
h3.classList.add("tertiary_heading");
h3.textContent = items.heading;
const ul = document.createElement("ul");
ul.classList.add("list_items");
const origin = window.location.origin;
items.list.forEach((item) => {
const li = document.createElement("li");
const link = document.createElement("a");
if (item.count > 0) {
link.href = `${origin}/${item.url}`;
link.textContent = item.breed;
li.append(link);
} else if (item.count === 0) {
li.textContent = item.breed;
}
ul.append(li);
});
article.append(h3, ul);
columns.append(article);
});
root.append(columns);
}
async function init() {
const data = await fetchDogBreeds();
if (!data) return;
const transformed = initTransformData(data);
initRenderUI(transformed);
}
init();