Skip to content

Instantly share code, notes, and snippets.

@typeofweb
Created January 21, 2022 15:29
Show Gist options
  • Save typeofweb/9afd9b73b2cd81c5b202f11fb4f84608 to your computer and use it in GitHub Desktop.
Save typeofweb/9afd9b73b2cd81c5b202f11fb4f84608 to your computer and use it in GitHub Desktop.
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