Created
January 21, 2022 15:29
-
-
Save typeofweb/9afd9b73b2cd81c5b202f11fb4f84608 to your computer and use it in GitHub Desktop.
This file contains 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
import Fetch from 'node-fetch'; | |
interface JJITOffer { | |
title: string; | |
street: string; | |
city: string; | |
country_code: string; | |
address_text: string; | |
marker_icon: string; | |
workplace_type: string; | |
company_name: string; | |
company_url: string; | |
company_size: string; | |
experience_level: string; | |
latitude: string; | |
longitude: string; | |
published_at: string; | |
remote_interview: boolean; | |
id: string; | |
employment_types: EmploymentType[]; | |
company_logo_url: string; | |
skills: Skill[]; | |
remote: boolean; | |
} | |
interface EmploymentType { | |
type: "mandate_contract" | "permanent" | "b2b"; | |
salary: null | { from: number; to: number; currency: "pln" | "usd" | "eur" }; | |
} | |
interface EmploymentTypePln { | |
type: "mandate_contract" | "permanent" | "b2b"; | |
salary: null | { from: number; to: number; currency: "pln" }; | |
} | |
interface Skill { | |
name: string; | |
level: number; | |
} | |
const notNil = <T>(el: T | null | undefined): el is T => el != null; | |
const average = (ss: Array<number | undefined | null>) => { | |
const filtered = ss.filter(notNil); | |
return filtered.reduce((a, b) => a + b, 0) / filtered.length; | |
} | |
const by = <Key extends string, Obj extends {[K in Key]: unknown}>(arr: readonly (Obj | null | undefined)[], key: Key) => arr.map(el => el?.[key]); | |
const hasSalary = (o: JJITOffer) => o.employment_types.some(t => !!t.salary); | |
const requiresJavaScript = (o: JJITOffer) => o.skills.some(skill => skill.name.toLowerCase() === 'javascript') | |
const levelJunior = (o: JJITOffer) => o.experience_level === 'junior' | |
const takeEmploymentTypes = (o: JJITOffer) => o.employment_types; | |
const convertCurrency = (e: EmploymentType): EmploymentTypePln | null => { | |
switch(e.salary?.currency) { | |
case 'pln': | |
return e as EmploymentTypePln; | |
case 'eur': | |
return {...e, salary: { to: e.salary.to * 4.54, from: e.salary.from * 4.54, currency: 'pln' }} | |
case 'eur': | |
return {...e, salary: { to: e.salary.to * 4.00, from: e.salary.from * 4.00, currency: 'pln' }} | |
} | |
return null; | |
} | |
const res = await Fetch('https://justjoin.it/api/offers'); | |
const offers = await res.json() as readonly JJITOffer[]; | |
const salariesByEmploymentType = | |
offers | |
.filter(levelJunior) | |
.filter(hasSalary) | |
.filter(requiresJavaScript) | |
.flatMap(takeEmploymentTypes) | |
.map(convertCurrency) | |
.filter(notNil) | |
.reduce((acc: Partial<Record<EmploymentTypePln['type'], EmploymentTypePln['salary'][]>>, el) => { | |
acc[el.type] = acc[el.type] || []; | |
acc[el.type]!.push(el.salary); | |
return acc; | |
}, {}); | |
const result = | |
Object.fromEntries( | |
Object.entries(salariesByEmploymentType).map(([type, salaries]) => { | |
const low = average(by(salaries, 'from')); | |
const high = average(by(salaries, 'to')); | |
const avg = (low + high) / 2; | |
return [type, { low: low.toFixed(2), high: high.toFixed(2), avg: avg.toFixed(2) }] as const; | |
}) | |
); | |
console.log(result); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment