The page says each box is a share of the people in this list. It printed one DOXA-wide figure - 0.6133% of 209,079,159 people - in every packet, true of no single list. That figure had no builder in the repo. build-evangelical-share.mjs makes its method reproducible, per group: the IMB CSV joined to the DOXA snapshot by ROP3 + country, each group's EvangelicalLevel band at its midpoint (0 for No Known Evangelicals, the midpoint read from the band's own wording), weighted by IMB population. It writes evangelical-by-slug.json and refuses to write unless the table, summed over every DOXA group, reproduces christian-share.json exactly: 2,091 matched, 209,079,159 people, 1,282,178 evangelical, 0.6133%. Change a single band's midpoint rule and it refuses. Not the API's imb_gsec: GSEC puts 225 groups at "no evangelicals" where EvangelicalLevel puts 1,444, because its "less than 2%" band also carries church-planting status. A share from it sits on a different scale from the method the page's own footnote states. evangelicalShare.js sums a packet's own groups - pure, zero imports, so the browser print and the offline generator run the same sum. Across the 28 zones the page now carries 28 different figures, from 0.182% (Deaf) to 0.922%. A host that supplies its own figure still wins. A list with no published levels prints a dash and no number. The old guard, Number.isFinite(Number(x)), passes null as 0 and would have printed "0% evangelical" for no data. Where some of a list's groups lack a level, the footnote says what the figure is computed over; it says nothing when coverage is whole. That clause is in all eleven languages. check-evangelical-share (gates:pre): the table reproduces the committed figure; different lists differ; no data is a dash; coverage appears only when partial. Mutation-tested four ways, each caught - the fourth only once it corrupted an existing group rather than adding one no sum reads.
41 lines
1.9 KiB
JavaScript
41 lines
1.9 KiB
JavaScript
/**
|
|
* evangelicalShare — the evangelical share of ONE packet's own people groups.
|
|
*
|
|
* The packet's "% evangelical" page says each box is a share of the people in this list. It printed a
|
|
* single DOXA-wide figure in every packet instead. This sums the packet's own groups, by the same method
|
|
* the DOXA-wide figure uses (each group's published EvangelicalLevel band at its midpoint, weighted by
|
|
* population — see library/scripts/people-groups/build-evangelical-share.mjs, whose table reproduces
|
|
* that figure exactly when summed over every DOXA group).
|
|
*
|
|
* Pure, zero imports, so the browser print and the offline generator run the identical sum.
|
|
*
|
|
* @param {Array<{slug:string}>} groups the packet's rows
|
|
* @param {{groups: Object<string,[number,number]>}} table evangelical-by-slug.json
|
|
* @returns {{groupsInList:number, groupsMatched:number, population:number, evangelicalPopulation:number,
|
|
* pctEvangelical:(number|null)}|null}
|
|
* pctEvangelical is null when none of the packet's groups has a published level: the page then prints
|
|
* a dash, never a number — above all never a DOXA-wide number presented as this list's.
|
|
* null for no table at all, so the page is omitted rather than drawn from nothing.
|
|
*/
|
|
export function evangelicalShareFor(groups, table) {
|
|
const bySlug = table && table.groups
|
|
if (!bySlug || typeof bySlug !== 'object') return null
|
|
const rows = Array.isArray(groups) ? groups : []
|
|
let population = 0, evangelical = 0, matched = 0
|
|
for (const g of rows) {
|
|
const e = g && bySlug[g.slug]
|
|
if (!Array.isArray(e)) continue
|
|
const [pct, pop] = e
|
|
if (!Number.isFinite(pct) || !Number.isFinite(pop)) continue
|
|
matched++
|
|
population += pop
|
|
evangelical += (pop * pct) / 100
|
|
}
|
|
return {
|
|
groupsInList: rows.length,
|
|
groupsMatched: matched,
|
|
population,
|
|
evangelicalPopulation: Math.round(evangelical),
|
|
pctEvangelical: population > 0 ? (100 * evangelical) / population : null,
|
|
}
|
|
}
|