Skip to content

Instantly share code, notes, and snippets.

@VonHeikemen
Last active December 28, 2020 18:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save VonHeikemen/8106f81058321dbff45eb6560d18e675 to your computer and use it in GitHub Desktop.
Save VonHeikemen/8106f81058321dbff45eb6560d18e675 to your computer and use it in GitHub Desktop.
Transducers (in javascript) in action.
// 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