Module 3 · Data, Variables, Samples, and Measurement Lesson 25 of 120
Identifiers, Keys, Joins, and Data Grain
A duplicate join can double the apparent money.
Transcript
22 sentences · select one to jump thereCheck your understanding
Is the sum function responsible for this doubled total?
Code lab
Run it yourself
The lesson source in 7 languages. Edit it, run TypeScript and Python right here, and compare with the expected output.
/**
* Fintech Math Bootcamp · Lesson 025 of 120
* Identifiers, Keys, Joins, and Data Grain
* Module 03: Data, Variables, Samples, and Measurement
*
* Scenario: A duplicate join can double the apparent money
* Rule: join cardinality must match the declared grain
*
* Try it: Is the sum function responsible for this doubled total?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*/
export function lesson025() {
const payments = [{id: "P1", customer: "C1", amount: 100}];
const profiles = [{customer: "C1"}, {customer: "C1"}];
const matches = payments.flatMap(p =>
profiles.filter(c => c.customer === p.customer).map(() => p));
const result = {joinedRows: matches.length,
apparentTotal: matches.reduce((s,p) => s+p.amount,0)};
return result;
}
export const checkedResult = {"joinedRows":2,"apparentTotal":200};
// Run this file directly: npx tsx lessons/03-data-variables-samples-and-measurement/025-identifiers-keys-joins-and-data-grain.ts
if (process.argv[1] && import.meta.url.endsWith(process.argv[1].replace(/\\/g, "/").split("/").pop()!)) {
console.log(JSON.stringify(lesson025(), null, 2));
}
Your output
Press Run to execute the code in your browser.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}"""
Fintech Math Bootcamp · Lesson 025 of 120
Identifiers, Keys, Joins, and Data Grain
Module 03: Data, Variables, Samples, and Measurement
Scenario: A duplicate join can double the apparent money
Rule: join cardinality must match the declared grain
Try it: Is the sum function responsible for this doubled total?
Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
Free course: https://courses.thefintechbuilder.com
Synthetic teaching example, not financial advice or a production library.
Run it: python main.py
"""
import json
def lesson025() -> dict:
payments = [{"id": "P1", "customer": "C1", "amount": 100}]
profiles = [{"customer": "C1"}, {"customer": "C1"}]
matches = [
p
for p in payments
for c in profiles
if c["customer"] == p["customer"]
]
result = {
"joinedRows": len(matches),
"apparentTotal": sum(p["amount"] for p in matches),
}
return result
if __name__ == "__main__":
print(json.dumps(lesson025(), indent=2))
Your output
Press Run to execute the code in your browser.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}/**
* Fintech Math Bootcamp · Lesson 025 of 120
* Identifiers, Keys, Joins, and Data Grain
* Module 03: Data, Variables, Samples, and Measurement
*
* Scenario: A duplicate join can double the apparent money
* Rule: join cardinality must match the declared grain
*
* Try it: Is the sum function responsible for this doubled total?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: javac Main.java && java Main
*/
import java.util.ArrayList;
import java.util.List;
public class Main {
static final class Payment {
final String id;
final String customer;
final double amount;
Payment(String id, String customer, double amount) {
this.id = id;
this.customer = customer;
this.amount = amount;
}
}
static final class Profile {
final String customer;
Profile(String customer) {
this.customer = customer;
}
}
static final class JoinCheck {
final int joinedRows;
final double apparentTotal;
JoinCheck(int joinedRows, double apparentTotal) {
this.joinedRows = joinedRows;
this.apparentTotal = apparentTotal;
}
}
static JoinCheck lesson025() {
Payment[] payments = {new Payment("P1", "C1", 100)};
Profile[] profiles = {new Profile("C1"), new Profile("C1")};
List<Payment> matches = new ArrayList<Payment>();
for (Payment p : payments) {
for (Profile c : profiles) {
if (c.customer.equals(p.customer)) matches.add(p);
}
}
double apparentTotal = 0;
for (Payment p : matches) apparentTotal += p.amount;
JoinCheck result = new JoinCheck(matches.size(), apparentTotal);
return result;
}
public static void main(String[] args) {
JoinCheck result = lesson025();
System.out.println(object(
"joinedRows", num(result.joinedRows),
"apparentTotal", num(result.apparentTotal)));
}
// Formats a double the way JSON.stringify does: whole numbers without ".0", null for NaN or infinity.
static String num(double x) {
if (Double.isNaN(x) || Double.isInfinite(x)) return "null";
if (x == Math.rint(x) && Math.abs(x) < 1e15) return String.valueOf((long) x);
return String.valueOf(x);
}
static String quote(String text) {
return "\"" + text.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
}
// Indents an already formatted JSON value by one level.
static String nested(String json) {
return json.replace("\n", "\n ");
}
static String object(String... keysAndValues) {
if (keysAndValues.length == 0) return "{}";
StringBuilder out = new StringBuilder("{");
for (int i = 0; i < keysAndValues.length; i += 2) {
out.append(i == 0 ? "\n " : ",\n ")
.append(quote(keysAndValues[i])).append(": ").append(nested(keysAndValues[i + 1]));
}
return out.append("\n}").toString();
}
}
No browser runner for Java yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}// Fintech Math Bootcamp · Lesson 025 of 120
// Identifiers, Keys, Joins, and Data Grain
// Module 03: Data, Variables, Samples, and Measurement
//
// Scenario: A duplicate join can double the apparent money
// Rule: join cardinality must match the declared grain
//
// Try it: Is the sum function responsible for this doubled total?
//
// Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
// Free course: https://courses.thefintechbuilder.com
// Synthetic teaching example, not financial advice or a production library.
//
// Run it: go run main.go
package main
import (
"encoding/json"
"fmt"
)
type Payment struct {
ID string
Customer string
Amount float64
}
type Profile struct {
Customer string
}
type JoinCheck struct {
JoinedRows int `json:"joinedRows"`
ApparentTotal float64 `json:"apparentTotal"`
}
func lesson025() JoinCheck {
payments := []Payment{{ID: "P1", Customer: "C1", Amount: 100}}
profiles := []Profile{{Customer: "C1"}, {Customer: "C1"}}
var matches []Payment
for _, p := range payments {
for _, c := range profiles {
if c.Customer == p.Customer {
matches = append(matches, p)
}
}
}
apparentTotal := 0.0
for _, p := range matches {
apparentTotal += p.Amount
}
result := JoinCheck{JoinedRows: len(matches), ApparentTotal: apparentTotal}
return result
}
func main() {
printJSON(lesson025())
}
// printJSON prints a value as JSON indented with two spaces, like JSON.stringify(value, null, 2).
func printJSON(value any) {
out, err := json.MarshalIndent(value, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(out))
}
No browser runner for Go yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}/**
* Fintech Math Bootcamp · Lesson 025 of 120
* Identifiers, Keys, Joins, and Data Grain
* Module 03: Data, Variables, Samples, and Measurement
*
* Scenario: A duplicate join can double the apparent money
* Rule: join cardinality must match the declared grain
*
* Try it: Is the sum function responsible for this doubled total?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: g++ -std=c++17 -o main main.cpp && ./main
*/
#include <charconv>
#include <cmath>
#include <iostream>
#include <string>
#include <utility>
#include <vector>
// Formats a double the way JSON.stringify does: shortest round-trip form, null for NaN or infinity.
std::string num(double x) {
if (!std::isfinite(x)) return "null";
char buffer[32];
auto [end, error] = std::to_chars(buffer, buffer + sizeof buffer, x);
(void)error;
return std::string(buffer, end);
}
std::string quote(const std::string& text) {
std::string out = "\"";
for (char c : text) {
if (c == '"' || c == '\\') out += '\\';
out += c;
}
return out + "\"";
}
// Indents an already formatted JSON value by one level.
std::string nested(const std::string& json) {
std::string out;
for (char c : json) {
out += c;
if (c == '\n') out += " ";
}
return out;
}
std::string object(const std::vector<std::pair<std::string, std::string>>& fields) {
if (fields.empty()) return "{}";
std::string out = "{";
for (std::size_t i = 0; i < fields.size(); ++i) {
out += i == 0 ? "\n " : ",\n ";
out += quote(fields[i].first) + ": " + nested(fields[i].second);
}
return out + "\n}";
}
struct Payment {
std::string id;
std::string customer;
double amount;
};
struct Profile {
std::string customer;
};
struct JoinCheck {
std::size_t joinedRows;
double apparentTotal;
};
JoinCheck lesson025() {
const std::vector<Payment> payments{{"P1", "C1", 100}};
const std::vector<Profile> profiles{{"C1"}, {"C1"}};
std::vector<Payment> matches;
for (const Payment& p : payments) {
for (const Profile& c : profiles) {
if (c.customer == p.customer) matches.push_back(p);
}
}
double apparentTotal = 0;
for (const Payment& p : matches) apparentTotal += p.amount;
const JoinCheck result{matches.size(), apparentTotal};
return result;
}
int main() {
const JoinCheck result = lesson025();
std::cout << object({
{"joinedRows", std::to_string(result.joinedRows)},
{"apparentTotal", num(result.apparentTotal)}
}) << "\n";
}
No browser runner for C++ yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}//! Fintech Math Bootcamp · Lesson 025 of 120
//! Identifiers, Keys, Joins, and Data Grain
//! Module 03: Data, Variables, Samples, and Measurement
//!
//! Scenario: A duplicate join can double the apparent money
//! Rule: join cardinality must match the declared grain
//!
//! Try it: Is the sum function responsible for this doubled total?
//!
//! Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
//! Free course: https://courses.thefintechbuilder.com
//! Synthetic teaching example, not financial advice or a production library.
//!
//! Run it: rustc main.rs && ./main
#[allow(dead_code)] // id identifies the payment but is not needed for the totals
#[derive(Clone, Copy)]
struct Payment {
id: &'static str,
customer: &'static str,
amount: f64,
}
struct Profile {
customer: &'static str,
}
struct JoinCheck {
joined_rows: usize,
apparent_total: f64,
}
fn lesson025() -> JoinCheck {
let payments = [Payment { id: "P1", customer: "C1", amount: 100.0 }];
let profiles = [Profile { customer: "C1" }, Profile { customer: "C1" }];
let matches: Vec<Payment> = payments
.iter()
.flat_map(|p| {
profiles
.iter()
.filter(move |c| c.customer == p.customer)
.map(move |_| *p)
})
.collect();
let result = JoinCheck {
joined_rows: matches.len(),
apparent_total: matches.iter().fold(0.0, |s, p| s + p.amount),
};
result
}
fn main() {
let result = lesson025();
println!(
"{}",
object(&[
("joinedRows", result.joined_rows.to_string()),
("apparentTotal", num(result.apparent_total)),
])
);
}
/// Formats a number the way JSON.stringify does: shortest round-trip form, null for NaN or infinity.
fn num(x: f64) -> String {
if x.is_finite() {
format!("{}", x)
} else {
"null".to_string()
}
}
fn quote(text: &str) -> String {
format!("\"{}\"", text.replace('\\', "\\\\").replace('"', "\\\""))
}
/// Indents an already formatted JSON value by one level.
fn nested(json: &str) -> String {
json.replace('\n', "\n ")
}
fn object(fields: &[(&str, String)]) -> String {
if fields.is_empty() {
return "{}".to_string();
}
let lines: Vec<String> = fields
.iter()
.map(|(key, value)| format!(" {}: {}", quote(key), nested(value)))
.collect();
format!("{{\n{}\n}}", lines.join(",\n"))
}
No browser runner for Rust yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}/**
* Fintech Math Bootcamp · Lesson 025 of 120
* Identifiers, Keys, Joins, and Data Grain
* Module 03: Data, Variables, Samples, and Measurement
*
* Scenario: A duplicate join can double the apparent money
* Rule: join cardinality must match the declared grain
*
* Try it: Is the sum function responsible for this doubled total?
*
* Lesson article: https://thefintechbuilder.com/financial-mathematics-statistics-and-data-foundations/data-variables-samples-and-measurement/identifiers-keys-joins-and-data-grain/
* Free course: https://courses.thefintechbuilder.com
* Synthetic teaching example, not financial advice or a production library.
*
* Run it: dotnet run (inside a console project that holds this Program.cs)
*/
using System.Text.Json;
var jsonOptions = new JsonSerializerOptions { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
Console.WriteLine(JsonSerializer.Serialize(Lesson025(), jsonOptions));
static JoinCheck Lesson025()
{
var payments = new[] { new Payment("P1", "C1", 100) };
var profiles = new[] { new Profile("C1"), new Profile("C1") };
var matches = payments
.SelectMany(p => profiles.Where(c => c.Customer == p.Customer).Select(_ => p))
.ToList();
var result = new JoinCheck(matches.Count, matches.Aggregate(0.0, (s, p) => s + p.Amount));
return result;
}
record Payment(string Id, string Customer, double Amount);
record Profile(string Customer);
record JoinCheck(int JoinedRows, double ApparentTotal);
No browser runner for C# yet
Read the code here, then run it in your own toolchain or a ready-made cloud workspace.
Expected output
{
"joinedRows": 2,
"apparentTotal": 200
}Prefer your own machine? Every file is in the course repository · open it in Codespaces.
Lesson notes
The rule
join cardinality must match the declared grain