DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

Cover image for Public Solving: Linked List and a train
Chris Bongers
Chris Bongers

Posted on • Originally published at daily-dev-tips.com

Public Solving: Linked List and a train

You might not know this, but the North Pole has a perfect train ecosystem.

However, due to the harsh weather, these trains need quite a lot of maintenance.

For this puzzle, we must figure out the train composition and add certain filters and actions to this composition.

You can find the puzzle here.

As our input, we get a linked list train object.
A linked list basically means an object with a next property that links to the next element.

For example:

export const locomotive = {
    name: 'L-283',
    emoji: '๐Ÿš‚',
    isLocomotive: true,
    next: wagon1,
}
const wagon1 = {
    name: 'W-10582',
    emoji: '๐Ÿš‹',
    lastBreakRevision: '2021-02-15',
    next: wagon2,
}
Enter fullscreen mode Exit fullscreen mode

As you can see, the locomotive has the wagon1 as it's next, which in return has wagon2, so by accessing the locomotive, we can composite the whole train.

Thinking about the solution

There are three things we need to work on:

  1. Complete the iterate wagons function and build the train
  2. Allow for the filter function to take place
  3. Allow for an action function to take place

We start with the following bootstrap function.

const defaultFilterFn = () => true
const defaultActionFn = wagon => console.log(`${wagon.emoji} ${wagon.name}`)

export const iterateWagons = (start, actionFn, filterFn) => {}

export const filterOldBreaks = wagon => {
    return true
}
Enter fullscreen mode Exit fullscreen mode

We have to fill out the iterate wagons function and the filter old breaks function.

The main challenge here is to convert the wagons into a train array following each next element of the train.

Then we will have to use array methods to filter and loop over this array we just created.

Note: It's been a while since I worked with Linked lists, so I'm pretty sure there are alternative ways of doing this assignment.

Building the linked list train in JavaScript

Let's convert our starting object into an array that follows the next order.

I've decided to use a while loop to look until the next property is empty.

const train = [start];
while (start.next !== null) {
    start = start.next;
    train.push(start);
}
Enter fullscreen mode Exit fullscreen mode

This sets the train to an array, starting with the locomotive.
Then, the while loop changes the start variable to be the next element and pushes it to our train array.

Making the while loop fire again since it won't be empty still.

Now this train array has the complete list of wagons in order!

The next part of the assignment is to make it possible to add specific filter criteria to each wagon.

Some of these criteria might be:

  • Check if the element is a locomotive
  • Check if the element brakes need replacement

We can use the filter method.
However, we'll need to use the default one if no filter is specified. We can set this in the parameters as the default.

export const iterateWagons = (
  start,
  actionFn,
  filterFn = defaultFilterFn
) => {
  const train = [start];
  while (start.next !== null) {
    start = start.next;
    train.push(start);
  }

  return train
    .filter((wagon) => filterFn(wagon));
};
Enter fullscreen mode Exit fullscreen mode

That will only return our train elements that match the provided filter, defaulting to everything.

The last part that remains for this function is the action.
We should pass an action on which something must happen per wagon.

We can use the same approach as the filter but leverage the forEach method.

export const iterateWagons = (
  start,
  actionFn = defaultActionFn,
  filterFn = defaultFilterFn
) => {
  const train = [start];
  while (start.next !== null) {
    start = start.next;
    train.push(start);
  }

  return train
    .filter((wagon) => filterFn(wagon))
    .forEach((wagon) => actionFn(wagon));
};
Enter fullscreen mode Exit fullscreen mode

The only thing we have to do now is create the filter for the old breaks.

A broken system is old when it hasn't been serviced for at least a year from today.

Note: again multiple ways to do this.

The first thing to note is that the wagons have the following date notation for the break service:

lastBreakRevision: '2021-02-15',
Enter fullscreen mode Exit fullscreen mode

Let's start by setting a new date variable and subtracting a year from that.

new Date(new Date().setFullYear(new Date().getFullYear() - 1))
// When running this on 10 December 2021 we get:
// 2020-12-10T05:10:51.846Z
Enter fullscreen mode Exit fullscreen mode

Almost there, we just need to remove the T05:10:51.846Z part.

To make that work, I plan to split it on the T and only return the first part.
This won't work because it's now a date object, and we need it to be a string.

That's where the .toISOString() comes into play.

export const filterOldBreaks = (wagon) => {
  return (
    new Date(new Date().setFullYear(new Date().getFullYear() - 1))
      .toISOString()
      .split('T')[0] > wagon.lastBreakRevision
  );
};
Enter fullscreen mode Exit fullscreen mode

And there you go, the complete break check function!

Let's run the test and see how we did:

Check on the test for this puzzle

I'm really keen to see how other people solved this puzzle, so do let me know ๐Ÿ‘

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (2)

Collapse
 
lexlohr profile image
Alex Lohr

That is too wonderful a chance to use ES6 generators to pass:

function* iterateLinkedList(start) {
    while (start) {
        yield start
        start = start.next
    }
}

function* filterLinkedList(iterator, filter) {
    for (const item of iterator) {
        if (filter(item)) { yield item }
    }
}

const linkedListForEach = (iterator, action) => {
    for (const item of iterator) {
        action(item)
    }
}

const iterateWagons = (start, actionFn, filterFn) => {
    linkedListForEach(
        filterLinkedList(
            iterateLinkedList(start),
            filterFn
        ),
        actionFn
    )
}
Enter fullscreen mode Exit fullscreen mode

This may be a bit more verbose than necessary, but it is very composable.

Also, Date objects coerce to their numeric timestamps when compared and Dates initialized from ISO dates have their hours/minutes/secods/milliseconds set to zero:

const todayLastYear = new Date()
todayLastYear.setFullYear(todayLastYear.getFullYear() - 1)

const filterOldBreaks = wagon => new Date(wagon.lastBrakeRevision || 0) < todayLastYear
Enter fullscreen mode Exit fullscreen mode
Collapse
 
dailydevtips1 profile image
Chris Bongers

Oh man!

I was hoping to get a response from you on this task!
This is wonderful!

Learned something new here!
As linked list are really a new field to me.

Gonna give this another try with you example soon ๐Ÿ‘

All DEV content is created by the community!

Hey, if you're landing here for the first time, you should know that this website is a global community of folks who blog about their experiences to help folks like you out.

Sign up now if you're curious. It's free!