caolan / caolan/highland

How to sell coworkers on FRP\Highland?

Open
#565 4 comments 0 reactions 0 assignees View on GitHub
cookbook discussion
Dominant language
JavaScript
Stars
3.4k
Forks
145
PR merge metrics
No merged PRs in 30d

Description

So at work I've been trying to sell the rest of the tech team on FRP & libraries like Highland to make it easier to accomplish harder tasks.

I found the perfect problem: If an event date is set in the future, how can we gradate the color between set intervals until then? For example we may want a teal-ish color when there are 48 hours or more until the event. Or a mix between teal & yellow when between 48 hours and 6 hours, and a mix between red & yellow when the time is less than 6 hours from the event date which is red.

Fortunately I was able to get it done in highland but looking at a comparison against one a coworker wrote in vanilla ES6 js, it feels like mine is a lot more verbose & makes less sense. Would anyone here have modeled it differently or is it that my grasp of FRP concepts is severely lacking?

Coworker's solution:

```js
const COLORS = [
{ stop: 0, rgb: [35, 198, 161] },
{ stop: 6, rgb: [245, 235, 73] },
{ stop: 48, rgb: [245, 51, 0] },
];

function getRGBColor (hours) {
let min, max, normalizationFactor;

for (let i = 0; i < COLORS.length; i++) {
if (hours <= COLORS[i].stop && hours >= COLORS[i-1].stop) {
max = COLORS[i];
min = COLORS[i-1];
break;
}
}

normalizationFactor = (hours - min.stop) / (max.stop - min.stop);

return min.rgb.map((c, i) => {
let rgb1 = min.rgb[i],
rgb2 = max.rgb[i],
normalizedDiff = (rgb2 - rgb1) * normalizationFactor,
rawValue = parseInt(rgb1 + normalizedDiff, 10);

if (rawValue > 255) return 255;
else if (rawValue < 0) return 0;

return rawValue;
});
}
```

My highland\FRP solution:

```js
const COLOR_TABLE = [
{
rgb: [245, 51, 0], // red
stop: 0,
},
{
rgb: [248, 235, 73], // yellow
stop: 6,
},
{
rgb: [44, 163, 178], // turk-like colors
stop: 48,
},
];

function calcGradient (input) {
return _([ input ])
// Calculate the duration & hours of the time between now & the event
.map((data) => {
data.duration = data.eventDate - data.now;
data.hours = Math.ceil(data.duration / 1000 / 60 / 60);
return data;
})
// Get the two colors we need to switch to
.flatMap((data) => {
return _(COLOR_TABLE)
// Takes colors until we no longer match their stop
.reduce({ done: false, colors: [] }, (reduction, color) => {
// A very cheap way to not use an if statement :D
!reduction.done && reduction.colors.push(color);

reduction.done = data.hours < color.stop;

return reduction;
})
// grab the colors property from the data object
.pluck('colors')
// Ensures there are always at least 2 colors in the event we're still
// before 48 hours or after the event has occurred
.map((colors) => [colors[0]].concat(colors))
// Only use the last two items.
.map((colors) => colors.slice(-2))
// Merge in our colors back into mainstream's our data
.map(([colorA, colorB]) => Object.assign(data, {
colors: {
prev: colorB,
next: colorA,
},
}));
})
// Get our progress factor to so we know where to transition between the
// two colors
.map((data) => {
let { hours, colors: { prev, next } } = data;
let normalProgress = (hours - prev.stop) / (next.stop - prev.stop);

return Object.assign(data, {
progress: Math.min(Math.max(normalProgress, 0), 1),
});
})
// Find a color that is transitional between the two colors
.flatMap((data) => {
let { colors: { prev, next }, progress } = data;

return _(( prev.rgb ))
.zip(next.rgb)
.map(([ start, end ]) => start + ((end - start) * progress))
.map(Math.floor)
.collect()
.map((gradient) => ({
gradient,
hours: data.hours,
time: data.now,
}));
})
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.