function getHorizontalBarChartOf(values, colors={}, width=400) {
const uniqueValues = [...new Set(values)]
const parsedOptions = uniqueValues.map((value, i) => ({
value,
color: colors[value] || d3.schemeTableau10[i],
count: values.filter(d => d === value).length,
}))
.sort((a, b) => b["count"] - a["count"])
const barHeight = 20
const marginBottom = 20
const height = barHeight * parsedOptions.length + marginBottom
const boundedHeight = height - marginBottom
const marginLeft = 150
const boundedWidth = width - marginLeft
const xScale = d3.scaleLinear()
.domain([0, d3.max(parsedOptions, d => d["count"])]).nice()
.range([0, boundedWidth])
const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.style("overflow", "visible")
const bounds = svg.append("g")
.attr("transform", move(marginLeft))
const xAxis = g => g
.attr("transform", move(0, boundedHeight))
.call(d3.axisBottom(xScale).ticks(3))
bounds.append("g")
.call(xAxis);
svg.selectAll(".label")
.data(parsedOptions)
.join("text")
.attr("class", "label")
.attr("y", (metric, i) => (i + 0.5) * barHeight)
.text(d => d["value"])
.style("font-family", "sans-serif")
.style("font-size", "12px")
.style("dominant-baseline", "middle")
.style("pointer-events", "none")
bounds.append("g")
.selectAll("rect")
.data(parsedOptions)
.join("rect")
.attr("x", 0)
.attr("y", (d, i) => i * barHeight)
.attr("width", d => xScale(d["count"]))
.attr("height", barHeight - 1)
.attr("fill", d => d["color"])
bounds.append("g")
.attr("fill", "white")
.attr("text-anchor", "end")
.attr("font-family", "sans-serif")
.attr("font-size", 12)
.selectAll(".value")
.data(parsedOptions)
.join("text")
.attr("class", "value")
.attr("x", d => xScale(d["count"]))
.attr("y", (d, i) => (i + 0.5) * barHeight)
.attr("dy", "0.35em")
.attr("dx", -4)
.text(d => d["count"])
.call(text => text.filter(d => xScale(d["count"]) < 20)
.attr("dx", +4)
.attr("fill", "black")
.attr("text-anchor", "start"));
return svg.node();
}