Three recipes for working with structured data - filtering CSV datasets, converting JSON API responses to CSV, and generating formatted reports from multiple sources.
Modules: CSV · List · Dictionary · File · String · Konsol
Reads products.csv, keeps only rows matching a given category, and writes the filtered rows to filtered_<category>.csv. Demonstrates the core CSV read/filter/write loop that underpins most data-pipeline scripts.
Key patterns:
CSV:Parse(content, doc) - parse raw CSV string into a named documentCSV:Rows(doc, outN) - row count; row 0 is the headerCSV:Get(row, col, doc, outStr) - read individual cells by indexCSV:Set + CSV:Stringify - build the filtered output document in memoryCSV:Free(doc) - release both input and output documents when doneShips with products.csv (sample product catalogue).
Usage
minks csv_filter.ks <category>
minks csv_filter.ks electronics
minks csv_filter.ks clothing
Sample output
Loaded 8 rows. Filtering by category: electronics
Matched 3 row(s) → filtered_electronics.csv
Done.
Sample data
name,category,price,stock
Widget Pro,electronics,29.99,150
USB Hub,electronics,12.50,320
Headphones,electronics,79.99,60
Monitor Stand,electronics,45.00,95
Office Chair,furniture,199.00,45
Desk Lamp,furniture,34.99,80
Notebook,stationery,4.99,500
Pen Set,stationery,8.99,250
Script
// csv_filter.ks - Filter rows in a CSV file by category and write matches to a new file
// Usage: minks csv_filter.ks <input.csv> <category>
//
// Try with the bundled sample:
// minks csv_filter.ks products.csv electronics
// minks csv_filter.ks products.csv furniture
//
// The script reads a CSV with headers: name, category, price, stock
// Rows where the category column matches the given value are written to
// filtered_<category>.csv in the current directory.
//
// Modules: CSV, File, String, List, OS
// ── 1. Parse arguments ────────────────────────────────────────────────────────
List:New args:String;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 2) {
Konsol:Print("Usage: minks csv_filter.ks <input.csv> <category>");
Konsol:Exit(1);
}
Var:String inputFile;
Var:String filterCategory;
List:Get(0, args, inputFile);
List:Get(1, args, filterCategory);
// ── 2. Read and parse the CSV ─────────────────────────────────────────────────
// File:ReadAll reads the entire file into a string in one call.
// CSV:Parse loads that string into a named in-memory document.
// The document name (here: inp) is a bare identifier, not a quoted string.
Var:Boolean fileExists;
File:Exists(inputFile, fileExists);
if (fileExists == false) {
Konsol:Print("File not found: ${inputFile}");
Konsol:Exit(1);
}
Var:Number fh;
Var:String content;
File:Open(inputFile, "r", fh);
File:ReadAll(fh, content);
File:Close(fh);
CSV:Parse(content, inp);
Var:Number totalRows;
CSV:Rows(inp, totalRows);
Var:Number dataRows = totalRows - 1;
Konsol:Print("Scanning ${dataRows} rows for category '${filterCategory}'...");
// ── 3. Copy the header into the output document ───────────────────────────────
// Cells are always strings; rows and columns are 0-based.
Var:String hName; Var:String hCategory; Var:String hPrice; Var:String hStock;
CSV:Get(0, 0, inp, hName);
CSV:Get(0, 1, inp, hCategory);
CSV:Get(0, 2, inp, hPrice);
CSV:Get(0, 3, inp, hStock);
// CSV:Set extends the output document (out) on demand - no need to pre-size it.
CSV:Set(0, 0, hName, out);
CSV:Set(0, 1, hCategory, out);
CSV:Set(0, 2, hPrice, out);
CSV:Set(0, 3, hStock, out);
// ── 4. Filter rows where column 1 (category) matches the argument ─────────────
Var:Number i = 1; // start at 1 to skip the header row
Var:Number outRow = 1; // next available row in the output document
Var:Number matched = 0;
Var:String rowName; Var:String rowCategory; Var:String rowPrice; Var:String rowStock;
while (i < totalRows) {
CSV:Get(i, 0, inp, rowName);
CSV:Get(i, 1, inp, rowCategory);
CSV:Get(i, 2, inp, rowPrice);
CSV:Get(i, 3, inp, rowStock);
// == compares string values directly.
if (rowCategory == filterCategory) {
CSV:Set(outRow, 0, rowName, out);
CSV:Set(outRow, 1, rowCategory, out);
CSV:Set(outRow, 2, rowPrice, out);
CSV:Set(outRow, 3, rowStock, out);
outRow = outRow + 1;
matched = matched + 1;
Konsol:Print(" match: ${rowName}");
}
i = i + 1;
}
Konsol:Print("${matched} row(s) matched.");
// ── 5. Write the output CSV ───────────────────────────────────────────────────
// Only write if there is at least one match.
if (matched > 0) {
// Build the output filename: "filtered_" + category + ".csv"
Var:String stem;
Var:String outPath;
String:Join("_", "filtered", filterCategory, stem);
String:Join("", stem, ".csv", outPath);
Var:String csv;
CSV:Stringify(out, csv);
Var:Number wh;
File:Open(outPath, "w", wh);
File:Write(csv, wh);
File:Close(wh);
Konsol:Print("Written to: ${outPath}");
}
CSV:Free(inp);
CSV:Free(out);
Modules: JSON · Dictionary · File · Konsol
Reads users.json (a JSON array under the "users" key), extracts each user's fields by zero-based index path, and writes a flat users.csv.
Key patterns:
JSON:Parse(content, doc) - parse the raw JSON stringJSON:Length("users", doc, outN) - count array elements at a named pathJSON:Get("users.${i}.field", doc, outStr) - interpolate the index into the pathFile:WriteShips with users.json (sample user list).
Usage
minks json_to_csv.ks
Sample output
Converting users.json → users.csv
Wrote 5 user(s).
Done.
Sample data
{
"users": [
{"id": 1, "name": "Alice Chen", "email": "alice@example.com", "role": "admin"},
{"id": 2, "name": "Bob Torres", "email": "bob@example.com", "role": "user"},
{"id": 3, "name": "Carol Kim", "email": "carol@example.com", "role": "user"},
{"id": 4, "name": "Dave Patel", "email": "dave@example.com", "role": "moderator"},
{"id": 5, "name": "Eve Johnson", "email": "eve@example.com", "role": "user"},
{"id": 6, "name": "Frank Müller", "email": "frank@example.com", "role": "moderator"},
{"id": 7, "name": "Grace Okafor", "email": "grace@example.com", "role": "user"}
]
}
Script
// json_to_csv.ks - Read a JSON array of objects and write selected fields to CSV
// Usage: minks json_to_csv.ks <input.json> <output.csv>
//
// Try with the bundled sample:
// minks json_to_csv.ks users.json users.csv
//
// Expects a JSON structure with a top-level "users" array where each element
// has: id, name, email, role.
// Extend the field list at the bottom to reshape for your own JSON shape.
//
// Modules: JSON, CSV, File, String, List, OS
// ── 1. Parse arguments ────────────────────────────────────────────────────────
List:New args:String;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 2) {
Konsol:Print("Usage: minks json_to_csv.ks <input.json> <output.csv>");
Konsol:Exit(1);
}
Var:String jsonFile;
Var:String csvFile;
List:Get(0, args, jsonFile);
List:Get(1, args, csvFile);
// ── 2. Read and parse the JSON file ──────────────────────────────────────────
// JSON:Parse loads the JSON string into a named in-memory document.
// The document name (here: doc) is a bare identifier, not a quoted string.
Var:Boolean fileExists;
File:Exists(jsonFile, fileExists);
if (fileExists == false) {
Konsol:Print("File not found: ${jsonFile}");
Konsol:Exit(1);
}
Var:Number fh;
Var:String content;
File:Open(jsonFile, "r", fh);
File:ReadAll(fh, content);
File:Close(fh);
JSON:Parse(content, doc);
// Get the number of elements in the "users" array.
Var:Number userCount;
JSON:Length("users", doc, userCount);
Konsol:Print("Found ${userCount} users in '${jsonFile}'.");
// ── 3. Write the CSV header ───────────────────────────────────────────────────
// CSV documents (here: out) are named bare identifiers.
CSV:Set(0, 0, "id", out);
CSV:Set(0, 1, "name", out);
CSV:Set(0, 2, "email", out);
CSV:Set(0, 3, "role", out);
// ── 4. Iterate the JSON array and populate the CSV ───────────────────────────
// JSON:Get uses dot-notation paths. Numeric segments index arrays:
// "users.0.name" → first user's name
// "users.1.email" → second user's email
// String interpolation (${i}) builds these paths dynamically.
Var:Number i = 0;
Var:Number csvRow = 1; // row 0 is the header
Var:String userId; Var:String userName; Var:String userEmail; Var:String userRole;
while (i < userCount) {
JSON:Get("users.${i}.id", doc, userId);
JSON:Get("users.${i}.name", doc, userName);
JSON:Get("users.${i}.email", doc, userEmail);
JSON:Get("users.${i}.role", doc, userRole);
CSV:Set(csvRow, 0, userId, out);
CSV:Set(csvRow, 1, userName, out);
CSV:Set(csvRow, 2, userEmail, out);
CSV:Set(csvRow, 3, userRole, out);
csvRow = csvRow + 1;
i = i + 1;
}
// ── 5. Serialize and write the CSV file ──────────────────────────────────────
Var:String csvContent;
CSV:Stringify(out, csvContent);
Var:Number wh;
File:Open(csvFile, "w", wh);
File:Write(csvContent, wh);
File:Close(wh);
Konsol:Print("Written ${userCount} row(s) to '${csvFile}'.");
JSON:Free(doc);
CSV:Free(out);
Modules: CSV · JSON · String · File · Dictionary · Konsol
Combines both sources - products.csv and users.json - into a single formatted text report: per-category product totals and a breakdown of users by role.
Key patterns:
Dictionary:Has / Dictionary:Get / Dictionary:Set - accumulate category counts into a DictionaryJSON:Get("users.${j}.role", users, role) - group users by role fieldFile:Write("...\n", wh) - write the report line by line to a text fileUsage
minks report_generator.ks
Sample output
Generating report...
Products: 8 rows across 3 categories.
Users: 5 records across 2 roles.
Report written to report.txt
Done.
Script
// report_generator.ks - Combine a CSV and a JSON source into a formatted text report
// Usage: minks report_generator.ks <products.csv> <users.json> <report.txt>
//
// Try with the bundled samples:
// minks report_generator.ks products.csv users.json report.txt
//
// Reads:
// products.csv - name, category, price, stock
// users.json - { "users": [ { id, name, email, role } ... ] }
//
// Writes a formatted report.txt with:
// - Product count grouped by category (CSV → Dictionary)
// - User count grouped by role (JSON → Dictionary)
//
// Modules: CSV, JSON, Dictionary, List, File, String, OS
// ── 1. Parse arguments ────────────────────────────────────────────────────────
List:New args:String;
OS:Args(args);
Var:Number argc;
List:Size(args, argc);
if (argc < 3) {
Konsol:Print("Usage: minks report_generator.ks <products.csv> <users.json> <report.txt>");
Konsol:Exit(1);
}
Var:String csvFile;
Var:String jsonFile;
Var:String reportFile;
List:Get(0, args, csvFile);
List:Get(1, args, jsonFile);
List:Get(2, args, reportFile);
// ── 2. Load the CSV ───────────────────────────────────────────────────────────
Var:Boolean fileExists;
File:Exists(csvFile, fileExists);
if (fileExists == false) {
Konsol:Print("CSV not found: ${csvFile}");
Konsol:Exit(1);
}
Var:Number fh;
Var:String content;
File:Open(csvFile, "r", fh);
File:ReadAll(fh, content);
File:Close(fh);
CSV:Parse(content, products);
Var:Number productRows;
CSV:Rows(products, productRows);
// ── 3. Group products by category using a Dictionary ──────────────────────────
// Dictionary:New creates an empty string-keyed dictionary.
// Dictionary:Has checks whether a key already exists (so we can initialize or increment).
Dictionary:New catCounts;
Var:Number i = 1; // row 0 is the header
Var:String cat;
Var:Boolean hasKey;
Var:Number catCount;
while (i < productRows) {
CSV:Get(i, 1, products, cat); // column 1 = category
Dictionary:Has(cat, catCounts, hasKey);
if (hasKey) {
Dictionary:Get(cat, catCounts, catCount);
catCount = catCount + 1;
} else {
catCount = 1;
}
Dictionary:Set(cat, catCount, catCounts);
i = i + 1;
}
// ── 4. Load the JSON ──────────────────────────────────────────────────────────
File:Exists(jsonFile, fileExists);
if (fileExists == false) {
Konsol:Print("JSON not found: ${jsonFile}");
Konsol:Exit(1);
}
Var:Number fh2;
Var:String jsonContent;
File:Open(jsonFile, "r", fh2);
File:ReadAll(fh2, jsonContent);
File:Close(fh2);
JSON:Parse(jsonContent, users);
Var:Number userCount;
JSON:Length("users", users, userCount);
// ── 5. Group users by role using a second Dictionary ──────────────────────────
Dictionary:New roleCounts;
Var:Number j = 0;
Var:String role;
Var:Boolean hasRole;
Var:Number roleCount;
while (j < userCount) {
JSON:Get("users.${j}.role", users, role);
Dictionary:Has(role, roleCounts, hasRole);
if (hasRole) {
Dictionary:Get(role, roleCounts, roleCount);
roleCount = roleCount + 1;
} else {
roleCount = 1;
}
Dictionary:Set(role, roleCount, roleCounts);
j = j + 1;
}
// ── 6. Write the report ───────────────────────────────────────────────────────
// File:Write appends the string argument to the open file handle.
// \n is the newline escape inside string literals.
Var:Number wh;
File:Open(reportFile, "w", wh);
File:Write("==============================\n", wh);
File:Write(" Combined Data Report\n", wh);
File:Write("==============================\n\n", wh);
// Products section - iterate Dictionary:Keys to print each category count.
File:Write("Products by Category\n", wh);
File:Write("--------------------\n", wh);
List:New catKeys:String;
Dictionary:Keys(catCounts, catKeys);
Var:Number catKeyCount;
List:Size(catKeys, catKeyCount);
Var:Number k = 0;
Var:String catKey;
Var:Number catVal;
while (k < catKeyCount) {
List:Get(k, catKeys, catKey);
Dictionary:Get(catKey, catCounts, catVal);
File:Write(" ${catKey}: ${catVal} item(s)\n", wh);
k = k + 1;
}
Var:Number totalProducts = productRows - 1;
File:Write(" ──\n", wh);
File:Write(" Total: ${totalProducts} product(s)\n\n", wh);
// Users section
File:Write("Users by Role\n", wh);
File:Write("-------------\n", wh);
List:New roleKeys:String;
Dictionary:Keys(roleCounts, roleKeys);
Var:Number roleKeyCount;
List:Size(roleKeys, roleKeyCount);
Var:Number m = 0;
Var:String roleKey;
Var:Number roleVal;
while (m < roleKeyCount) {
List:Get(m, roleKeys, roleKey);
Dictionary:Get(roleKey, roleCounts, roleVal);
File:Write(" ${roleKey}: ${roleVal} user(s)\n", wh);
m = m + 1;
}
File:Write(" ──\n", wh);
File:Write(" Total: ${userCount} user(s)\n", wh);
File:Close(wh);
Konsol:Print("Report written to '${reportFile}'.");
CSV:Free(products);
JSON:Free(users);