Published
Edited
May 18, 2021
1 fork
Importers
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cities = FileAttachment("cities.json").json()
Insert cell
Insert cell
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
Insert cell
Insert cell
pipeline = [
filterByClimate, // quickly limit list to short list of cities with proper climate
updateTemperatureOfCities, // convert city’s temperature from Kelvin to Celcius
cityScorer, // score each city based on cost and internet speed
citySorter, // sort the list of cities by score
cityGrabber, //†r grab its top 10 cities
]
Insert cell
Insert cell
cityTop10 = pipe(...pipeline)(cities) // not sure why, but w/o the reverse(), array is empty
Insert cell
Insert cell
Insert cell
Insert cell
bangkok = cities.find((city) => city.name = "Bangkok")
Insert cell
Insert cell
CtoK = k => k + 273.15
Insert cell
CtoK(0)
Insert cell
FtoK = k => (k - 32) * 5 / 9 + 273.15
Insert cell
FtoK(100)
Insert cell
Insert cell
KtoC = k => k - 273.15
Insert cell
KtoC(300)
Insert cell
KtoF = k => k * 9 / 5 - 459.67
Insert cell
KtoF(300)
Insert cell
Insert cell
Insert cell
KtoC(bangkok.temp)
Insert cell
Insert cell
ranges = ({
Kelvin: map(CtoK, [20, 30]),
humidity: [30, 70],
})
Insert cell
Insert cell
byClimatePredicate = curry((ranges, city) => {
const { temp = 0, humidity = 0 } = city
return withinRange(ranges.Kelvin, temp) && withinRange(ranges.humidity, humidity)
})
Insert cell
withinRange = (range, value) => (range[0] <= value && value <= range[1])
Insert cell
Insert cell
filterByClimate = filter(byClimatePredicate(ranges))
Insert cell
Insert cell
citiesWithGoodClimate = filterByClimate(cities)
Insert cell
Insert cell
Insert cell
Insert cell
temperatureConverter = KtoC // set to KtoF if you prefer Fahrenheit
Insert cell
Insert cell
updateTemperature = curry(
(temperatureConverter, city) =>
({
...city,
temp: Math.round(temperatureConverter(city.temp))
})
)
Insert cell
Insert cell
toMyPreferredTemperatureUnitConverter = updateTemperature(temperatureConverter)
Insert cell
Insert cell
toMyPreferredTemperatureUnitConverter(bangkok).temp
Insert cell
Insert cell
citiesWithGoodClimate.map(toMyPreferredTemperatureUnitConverter)
Insert cell
Insert cell
map(toMyPreferredTemperatureUnitConverter, citiesWithGoodClimate)
Insert cell
Insert cell
updateTemperatureOfCities = map(toMyPreferredTemperatureUnitConverter)
Insert cell
citiesWithGoodClimateInCelcius = updateTemperatureOfCities(citiesWithGoodClimate)
Insert cell
Insert cell
Insert cell
Insert cell
cityCostSummer = (sum, city) => sum + city.cost ?? 0 // if city has no cost specfied, use 0
Insert cell
Insert cell
citiesWithGoodClimateInCelcius.reduce(cityCostSummer, 0)
Insert cell
Insert cell
averageCostTraditional = list => list.reduce(cityCostSummer, 0) / list.length
Insert cell
averageCostTraditional(citiesWithGoodClimateInCelcius)
Insert cell
Insert cell
Insert cell
Insert cell
propArray = curry(
(prop, list) =>
({
[prop]: map(k => k[prop], list)
})
)
Insert cell
Insert cell
costs = propArray("cost")
Insert cell
Insert cell
cityCostsObject = costs(citiesWithGoodClimateInCelcius)
Insert cell
Insert cell
cityCosts = cityCostsObject.cost
Insert cell
Insert cell
average = (numbers) => sum(numbers) / numbers.length
Insert cell
average(cityCosts)
Insert cell
Insert cell
properties = ["cost", "internetSpeed"]
Insert cell
Insert cell
transpose = curry((properties, list) => reduce(
(collector, item) =>
({
...collector,
...propArray(item)(list)
}),
{}
)(properties))
Insert cell
Insert cell
extractor = transpose(properties)
Insert cell
Insert cell
groupedByProp = extractor(citiesWithGoodClimateInCelcius)
Insert cell
Insert cell
percentile = curry(
(array, value) => (
reduce(
(collector, v) => collector
+ (v < value ? 1 : 0)
+ (v === value ? 0.5 : 0),
0,
array
)
) / array.length
)
Insert cell
Insert cell
percentiles = ({
cost: percentile(groupedByProp.cost),
internetSpeed: percentile(groupedByProp.internetSpeed),
})
Insert cell
percentiles.cost(3000)
Insert cell
citiesWithGoodClimateInCelcius[3].cost
Insert cell
score = (prop, item) => percentiles[prop](item[prop])
Insert cell
score("cost", bangkok)
Insert cell
scores = (prop) => map(item => score(prop, item))
Insert cell
scores("cost")(citiesWithGoodClimateInCelcius)
Insert cell
scoreMaker = (parameter, prop, scorer) => {
return ({
[prop]: {
percentile: percentile(parameter[prop].values),
scorer
}
})
}
Insert cell
Insert cell
cityScorer = map(city => {
const { cost = 0 , internetSpeed = 0 } = city // extract cost and internetspeed from city; 0 if not present
const costPercentile = percentile(groupedByProp.cost, cost)
const internetSpeedPercentile = percentile(groupedByProp.internetSpeed, internetSpeed)
const score = Math.round(
100 * (1 - costPercentile) +
20 * internetSpeedPercentile
)
return ({...city, score}) // return new city object with score added
})
Insert cell
Insert cell
citiesWithGoodClimateInCelciusAndScores = cityScorer(citiesWithGoodClimateInCelcius)
Insert cell
Insert cell
citySorter = cities => sortBy(city => city.score)(cities).reverse()
Insert cell
R.sortBy(city => city.score)(citiesWithGoodClimateInCelciusAndScores).reverse()
Insert cell
_.sortBy(city => city.score)(citiesWithGoodClimateInCelciusAndScores).reverse()
Insert cell
sortedCities = citySorter(citiesWithGoodClimateInCelciusAndScores)
Insert cell
cityGrabber = take(10)
Insert cell
cityGrabber(sortedCities)
Insert cell
Insert cell
cityTop10
Insert cell
Insert cell
Insert cell
Insert cell
compose(...pipeline.slice().reverse())(cities)
// do slice() before reverse() because reverse() reverses **in place**: side effect: kills you
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
lib = ({R:R, _:_})[library]
Insert cell
Insert cell
map = lib["map"]
Insert cell
curry = lib["curry"]
Insert cell
filter = lib["filter"]
Insert cell
sum = lib["sum"]
Insert cell
reduce = lib["reduce"]
Insert cell
sortBy = lib["sortBy"]
Insert cell
take = lib["take"]
Insert cell
pipe = lib["pipe"]
Insert cell
compose = lib["compose"]
Insert cell
Insert cell
R = require("ramda")
Insert cell
_ = { // source: https://observablehq.com/@mbostock/hello-lodash-fp
const [_, fp] = await Promise.all([
require("lodash@4"),
require("https://cdn.jsdelivr.net/gh/lodash/lodash@4/dist/lodash.fp.min.js")
]);
return fp(_);
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more