npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

data-arrganizer

v0.1.0

Published

The `Arrganizer` class is a utility for organizing, transforming, and formatting data sets. It provides various methods to group, filter, modify, and format data, as well as to manage the history of operations performed on the data.

Readme


Why Arrganizer?

Arrganizer is a zero-dependency TypeScript library that makes working with arrays of objects feel like working with a spreadsheet. If you've ever written long chains of .filter().map().reduce() just to organize your data, Arrganizer is for you.

What makes it different?

  • Fluent API — Chain operations like .groupByKey("city").sortByKey("revenue").take(10). Readable, composable, and concise.
  • Full history — Every operation is recorded. Undo/redo with a single call. No more lost states.
  • Built-in formatting — Locale-aware number, date, currency formatting via Intl. No external dependencies.
  • Type-safe — Full TypeScript support with generics. Your IDE knows your column names.
  • Spreadsheet-like summaries — Get totals, averages, min/max with getTables({total: true, average: true}).
  • Zero dependencies — Pure TypeScript. No bloat. 11KB gzipped.
  • Works everywhere — Node.js, browsers, Deno. ESM & CJS builds included.

When to use Arrganizer

| Scenario | Arrganizer | |----------|------------| | You have an array of objects and need to group, filter, sort, or aggregate it | ✅ Perfect | | You need undo/redo on data transformations | ✅ Built-in | | You want formatted table output (console, HTML, CSV) | ✅ getTables() | | You're building a data dashboard or reporting tool | ✅ Ideal | | You need complex SQL-like joins, pivots, and aggregations | ✅ Supported | | You're processing huge datasets (millions of rows) | ⚠️ Consider streaming |


Table of Contents

  1. Installation
  2. Quick Start
  3. Constructor
  4. Data Operations
  5. Column Operations
  6. Grouping & Filtering
  7. Transformation
  8. Aggregation
  9. Pagination
  10. Advanced Operations
  11. State Management
  12. Formatters
  13. Changelog

Installation

npm i data-arrganizer
import { Arrganizer } from 'data-arrganizer';

Quick Start

import { Arrganizer } from 'data-arrganizer';

const sales = [
  { product: "Laptop", region: "EU", revenue: 1200 },
  { product: "Phone", region: "EU", revenue: 800 },
  { product: "Laptop", region: "US", revenue: 1500 },
  { product: "Phone", region: "US", revenue: 950 },
  { product: "Tablet", region: "EU", revenue: 600 },
  { product: "Tablet", region: "US", revenue: 700 },
];

const a = new Arrganizer(sales);

// Group by region, sort by revenue, show top 3
const result = a
  .groupByKey("region")
  .sortByKey("revenue", "desc")
  .take(3)
  .getTables({ total: true });

console.log(result);

Constructor

new Arrganizer(data: DataRow[], options?: ArrganizerOptions)

Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | historySize | number | 10 | Max undo/redo history depth | | headerDictionary | Record<string, string> | - | Maps keys to display-friendly names | | cellFormats | Record<string, string \| Function> | - | Per-column formatting rules | | locale | string | "en" | Locale for formatting | | decimals | number | 3 | Decimal places for numbers |

Cell Format Types

"number", "date", "time", "datetime", "eur", "usd", "huf", or a custom function.

const a = new Arrganizer(data, {
  headerDictionary: { name: "Full Name", salary: "Annual Salary" },
  cellFormats: { salary: "usd", joined: "date" },
  locale: "en-US",
});

Data Operations

getTables

Returns formatted tables with optional summary rows.

const tables = a.getTables({ total: true, average: true, min: true });
// Returns: TableData[] with title, summary, and table (header + body)

getData

Returns the current internal state as DataSet[].

const datasets = a.getData();
console.log(datasets[0].data);    // Array of rows
console.log(datasets[0].summary); // { total, min, max, average, length }

getOriginalData

Returns the raw data passed to the constructor.

const original = a.getOriginalData();

getHistory

Returns the full undo/redo history stack.

const history = a.getHistory();
console.log(history.length); // Number of states stored

Column Operations

addKey

Adds a new computed column.

a.addKey("revenueWithTax", (row) => (row.revenue as number) * 1.27);
// Adds a "revenueWithTax" column to every row

renameKey

Renames a column.

a.renameKey("name", "fullName");
// "name" column is now called "fullName"

removeKey

Removes a single column.

a.removeKey("age");

removeKeys

Removes multiple columns.

a.removeKeys(["age", "salary", "department"]);

keepKeys

Keeps only specified columns, removes everything else.

a.keepKeys(["name", "revenue"]);

mergeColumns

Merges two columns using a custom function.

a.mergeColumns("firstName", "lastName", (first, last) => `${first} ${last}`, "fullName");
// "firstName" and "lastName" are replaced by "fullName"

reorderColumns

Reorders columns. If addMissing is true (default), unlisted columns are appended.

a.reorderColumns(["revenue", "product", "region"]);
a.reorderColumns(["revenue", "product"], false); // Only these two columns

Grouping & Filtering

groupByKey

Groups data by a single key into separate datasets.

a.groupByKey("region");
// Creates separate datasets for each unique region

groupByKeys

Groups by multiple keys (cross-product).

a.groupByKeys(["region", "product"]);
// Creates datasets for each region+product combination

groupByDate

Groups by date at a specified granularity.

a.groupByDate("MONTH", "orderDate");
// TimeRange: "YEAR" | "MONTH" | "WEEK" | "DAY" | "HOUR" | "MINUTE" | "SECOND"

filterByKey

Filters rows where the key matches any of the given values.

a.filterByKey("region", ["EU", "APAC"]);

contains

Filters rows where ANY column contains the given substring (case-insensitive).

a.contains("laptop");

where

Filters rows with a condition operator.

a.where("revenue", ">", 1000);
a.where("status", "===", "active");
a.where("name", "contains", "john");
// Operators: ">", "<", ">=", "<=", "===", "!==", "contains"

distinct

Returns unique values for a column (read-only, no mutation).

const regions = a.distinct("region");
// ["EU", "US", "APAC"]

Transformation

modifyValue

Transforms values in a specific column.

a.modifyValue("revenue", (val) => (val as number) * 1.1);

mapRows

Transforms every row with a custom function.

a.mapRows((row) => ({
  ...row,
  label: `${row.product} (${row.region})`,
  revenue: (row.revenue as number) * 1.1,
}));

sortByKey

Sorts by a key in ascending or descending order.

a.sortByKey("revenue", "desc");

Aggregation

count

Returns total row count across all datasets.

const total = a.count();

sum

Returns the sum of numeric values for a column.

const totalRevenue = a.sum("revenue");

mean

Returns the arithmetic mean for a column.

const avgRevenue = a.mean("revenue");

frequencyByKeysValue

Groups by keys and counts frequency. Optionally aggregates numeric columns.

a.frequencyByKeysValue(["region"]);
// Adds a "frequency" column with the count of each region

a.frequencyByKeysValue(["region"], "count", ["revenue"]);
// "count" column = frequency, "revenue" = sum of revenues per region

Pagination

take

Keeps only the first n rows per dataset.

a.take(10);

skip

Skips the first n rows per dataset.

a.skip(5);

head

Alias for take. Returns first n rows (default: 1).

a.head(5);

tail

Returns the last n rows per dataset (default: 1).

a.tail(5);

sample

Returns n random rows per dataset.

a.sample(3);

Advanced Operations

pivot

Transforms long-format data to wide-format.

const data = [
  { id: 1, metric: "revenue", value: 100 },
  { id: 1, metric: "cost", value: 50 },
  { id: 2, metric: "revenue", value: 200 },
  { id: 2, metric: "cost", value: 80 },
];

new Arrganizer(data).pivot("id", "metric", "value");
// Result:
// [{ id: 1, revenue: 100, cost: 50 },
//  { id: 2, revenue: 200, cost: 80 }]

join

Left-joins this dataset with another Arrganizer on a shared key.

const a = new Arrganizer([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]);
const b = new Arrganizer([{ id: 1, score: 95 }, { id: 2, score: 87 }]);

a.join(b, "id");
// [{ id: 1, name: "Alice", score: 95 },
//  { id: 2, name: "Bob", score: 87 }]

pipe

Composes operations through a pipeline of functions.

const process = (a) => a.groupByKey("region").sortByKey("revenue", "desc");

a.pipe(process, (a) => a.take(5));

diff

Compares two Arrganizer states and returns added, removed, and unchanged rows.

const a = new Arrganizer([{ id: 1 }, { id: 2 }, { id: 3 }]);
const b = new Arrganizer([{ id: 2 }, { id: 3 }, { id: 4 }]);

const { added, removed, unchanged } = a.diff(b);
// added: [{ id: 4 }]
// removed: [{ id: 1 }]
// unchanged: [{ id: 2 }, { id: 3 }]

State Management

clone / snapshot

Creates an independent deep copy of the current state.

const backup = a.clone();
a.take(3);
console.log(backup.getData()[0].data.length); // Still has all rows

toJSON / fromJSON

Serializes and deserializes the Arrganizer state.

const json = a.toJSON();
const restored = new Arrganizer([{ id: 0 }]).fromJSON(json);

reset

Resets to the original data and clears history.

a.groupByKey("region");
a.reset(); // Back to original data

undo / redo

Steps backward/forward through the operation history.

a.groupByKey("region");   // State 1
a.sortByKey("revenue");   // State 2
a.undo();                  // Back to State 1
a.redo();                  // Forward to State 2

Formatters

Arrganizer exports its formatters for standalone use:

import {
  formatNumbers,
  formatDate,
  formatTime,
  formatDateTime,
  getMonthName,
  getDayName,
  isValidLocale,
} from 'data-arrganizer';

formatNumbers(1234.56, { locale: "de-DE", currency: "EUR" }); // "1.234,56 €"
formatDate("2024-01-15", { locale: "hu-HU" });                // "2024. 01. 15."
getMonthName(new Date(2024, 0), "hu");                         // "január"
getDayName(new Date(2024, 0, 15), "hu", false);                // "hétfő"

Changelog

v0.0.3

New Features

  • addKey, renameKey — column operations
  • distinct, where — advanced querying
  • take, skip, head, tail, sample — pagination
  • mapRows — general row transformation
  • count, sum, mean — quick aggregations
  • clone, snapshot, toJSON, fromJSON — state management
  • pivot, join, pipe, diff — advanced operations
  • SECOND time range for groupByDate

Bug Fixes

  • Fixed uniqueKeys always returning true
  • Fixed history storing references instead of deep copies
  • Fixed mergeColumns/reorderColumns missing undo support
  • Fixed sortByKey closure variable bug
  • Fixed formatDataSets crash on empty data

Code Quality

  • Removed unused types (ArrganizerType, ArrganizerConstructor, FormattersType)
  • Exported formatter functions for standalone use
  • All pre-existing lint and test issues resolved

License

MIT