Last active
December 28, 2020 18:28
-
-
Save VonHeikemen/8106f81058321dbff45eb6560d18e675 to your computer and use it in GitHub Desktop.
Transducers (in javascript) in action.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Getting some stats from https://dev.to/dashboard | |
// Inspired by this: https://dev.to/tylerlwsmith/get-your-dev-2020-year-in-review-scraping-data-using-the-console-3gen | |
function compose(...fns) { | |
const apply = (arg, fn) => fn(arg); | |
return (initial) => fns.reduceRight(apply, initial); | |
} | |
function to_transducer(reducer) { | |
if(typeof reducer['@@transducer/step'] == 'function') { | |
return reducer; | |
} | |
return { | |
'@@transducer/init': function() { | |
throw new Error('Method not implemented'); | |
}, | |
'@@transducer/result': function(state) { | |
return state; | |
}, | |
'@@transducer/step': function(state, value) { | |
return reducer(state, value); | |
} | |
}; | |
} | |
function reduce(transducer, initial, collection) { | |
transducer = to_transducer(transducer); | |
if(arguments.length === 2) { | |
collection = initial; | |
initial = transducer['@@transducer/init'](); | |
} | |
let state = initial; | |
for(let value of collection) { | |
state = transducer['@@transducer/step'](state, value); | |
if(state != null && state['@@transducer/reduced']) { | |
state = state['@@transducer/value']; | |
break; | |
} | |
} | |
return transducer['@@transducer/result'](state); | |
} | |
function filter(predicate, next) { | |
if(arguments.length === 1) { | |
return (_next) => filter(predicate, _next); | |
} | |
return { | |
'@@transducer/init': function() { | |
return next['@@transducer/init'](); | |
}, | |
'@@transducer/result': function(state) { | |
return next['@@transducer/result'](state); | |
}, | |
'@@transducer/step': function(state, value) { | |
if(predicate(value)) { | |
return next['@@transducer/step'](state, value); | |
} | |
return state; | |
}, | |
}; | |
} | |
function map(transform, next) { | |
if(arguments.length === 1) { | |
return (_next) => map(transform, _next); | |
} | |
return { | |
'@@transducer/init': function() { | |
return next['@@transducer/init'](); | |
}, | |
'@@transducer/result': function(state) { | |
return next['@@transducer/result'](state); | |
}, | |
'@@transducer/step': function(state, value) { | |
return next['@@transducer/step'](state, transform(value)); | |
}, | |
}; | |
} | |
function transduce(combine, initial, transducer, collection) { | |
return reduce( | |
transducer(to_transducer(combine)), | |
initial, | |
collection | |
); | |
} | |
var ensure_number = (value) => | |
Number.isNaN(value) ? 0 : value; | |
var get_text = (element) => (query) => | |
element.querySelector(query).innerText; | |
var get_data = function(story) { | |
const text = get_text(story); | |
return { | |
published: new Date(story.querySelector('time').dateTime), | |
reactions: Number(text('[title="Reactions"]')), | |
comments: Number(text('[title="Comments"]')), | |
views: Number(text('[title="Views"]')), | |
}; | |
} | |
var published_2020 = function(story) { | |
return story.published.getFullYear() === 2020; | |
} | |
var add_stats = function(state, value) { | |
return { | |
reactions: state.reactions + value.reactions, | |
comments: state.comments + value.comments, | |
views: state.views + ensure_number(value.views) | |
}; | |
}; | |
var transducer = compose( | |
map(get_data), | |
filter(published_2020) | |
); | |
var stats = { | |
reactions: 0, | |
comments: 0, | |
views: 0, | |
}; | |
var stories = document.querySelectorAll( | |
'.dashboard-story:not(.story-unpublished)' | |
); | |
transduce(add_stats, stats, transducer, stories); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment