Skip to content

Migration Guide

This guide helps you transition to gaslamp from other approaches: raw GAS sheet operations, plain JavaScript arrays, or Python pandas.

Each section shows the before/after pattern so you can map your existing code to the equivalent gaslamp API.


Coming from Google Sheets (GAS raw API)

Before: Manual array processing

JavaScript
// Goal: filter rows where price > 1000, then write back to sheet
// Requires: manually tracking header index, looping with index arithmetic
function processData() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const data = sheet.getDataRange().getValues();

  const headers = data[0];
  const rows = data.slice(1);

  const priceIndex = headers.indexOf("price");

  const expensive = [];
  for (let i = 0; i < rows.length; i++) {
    if (rows[i][priceIndex] > 1000) {
      expensive.push(rows[i]);
    }
  }

  sheet.getRange(1, 1, expensive.length + 1, headers.length)
    .setValues([headers, ...expensive]);
}

After: BareFrame

JavaScript
// Goal: filter rows where price > 1000, then write back to sheet
// Simplified to: load sheet, filter by name, write back
function processData() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const df = gaslamp.BareFrame.fromSheet(sheet);

  const expensive = df.filter(row => row.get("price") > 1000);
  expensive.toSheet(sheet);
}

Coming from Raw Arrays / Objects

Use the factory that matches your data shape:

JavaScript
// 2D array with header row (e.g. getValues() output)
gaslamp.BareFrame.fromArrays(table);

// Column-oriented object
gaslamp.BareFrame.fromColumns({ name: ['Alice', 'Bob'], age: [25, 30] });

// Row-oriented Maps (converted from plain objects)
const rows = data.map(obj => new Map(Object.entries(obj)));
gaslamp.BareFrame.fromRows(rows);

// GAS Range object (first row as headers)
gaslamp.BareFrame.fromRange(sheet.getRange('A1:C10'));

// GAS Sheet object — reads entire data range
gaslamp.BareFrame.fromSheet(sheet);

Grouping and Data Aggregation

Before: Manual extraction

JavaScript
// Goal: compute total and average price per category
// Before: manually extract unique categories, loop + reduce per group — twice
function analyzeData(data) {
  const categories = [...new Set(data.map(row => row.category))];
  const result = {};
  categories.forEach(cat => {
    const items = data.filter(row => row.category === cat);
    const total = items.reduce((sum, row) => sum + row.price, 0);
    result[cat] = { sum: total, mean: total / items.length };
  });
  return result;
}

After: BareFrame.groupBy + agg methods

JavaScript
// Goal: compute total and average price per category
// After: groupBy + sum / mean — one call each, no looping
const df = gaslamp.BareFrame.fromColumns({
  category: ['Electronics', 'Clothing', 'Electronics'],
  price:    [500, 80, 1200],
});

const totalByCategory = df.groupBy(['category']).sum(['price']).rename({ price: 'total' });
// category     | total
// Electronics  | 1700
// Clothing     | 80

const avgByCategory = df.groupBy(['category']).mean(['price']).rename({ price: 'mean' });
// category     | mean
// Electronics  | 850
// Clothing     | 80

const summary = totalByCategory.join(avgByCategory, 'category');
// category     | total | mean
// Electronics  | 1700  | 850
// Clothing     | 80    | 80

Coming from Pandas

If you are familiar with Python pandas, the API will feel familiar:

Python
# Goal: filter by price, select columns, compute revenue
# Requires: boolean indexing, column assignment, chained operations
import pandas as pd

df = pd.DataFrame({
    'product':  ['A', 'B', 'C'],
    'price':    [100, 200, 150],
    'quantity': [5, 3, 8],
})

expensive = df[df.price > 120]
summary = expensive[['product', 'price', 'quantity']]
summary['revenue'] = summary.price * summary.quantity
JavaScript
// Goal: filter by price, select columns, compute revenue
// Simplified to: named method chain, no index management
const data = [
  ['product', 'price', 'quantity'],
  ['A', 100, 5],
  ['B', 200, 3],
  ['C', 150, 8],
];
const df = gaslamp.BareFrame.fromArrays(data);

const expensive = df.filter(row => row.get('price') > 120);
const summary = expensive.select(['product', 'price', 'quantity']);
const withRevenue = summary.withColumn(
  'revenue',
  row => row.get('price') * row.get('quantity'),
);

Method Equivalents

Operation Pandas gaslamp
Create from 2D array pd.DataFrame(data, columns=[...]) BareFrame.fromArrays(data)
Create from dict pd.DataFrame({'name': [...], 'age': [...]}) BareFrame.fromColumns({ name: [...], age: [...] })
Create from records pd.DataFrame([{'name': 'A', 'age': 1}]) BareFrame.fromRows([new Map([['name', 'A'], ['age', 1]])])
Filter rows df[df.price > 1000] df.filter(row => row.get('price') > 1000)
Select columns df[['name', 'price']] df.select(['name', 'price'])
Remove columns df.drop(['age'], axis=1) df.dropColumns(['age'])
Add column df['total'] = df.a * df.b df.withColumn('total', row => row.get('a') * row.get('b'))
Sort rows df.sort_values(['price']) df.sortBy(['price'])
Sort descending df.sort_values(['price'], ascending=False) df.sortBy(['price'], { ascending: [false] })
Group by df.groupby('category') df.groupBy(['category'])
Get column df['price'].tolist() df.getColumn('price')
Get row df.iloc[0] df.getRow(0)
Row count len(df) df.length

Common Pitfalls

Index-based access

JavaScript
// Avoid: manual index management
const nameIndex = headers.indexOf('name');
const name = row[nameIndex];

// Prefer: named access
const name = row.get('name');

Hardcoded column positions

JavaScript
// Avoid: brittle column access
const total = row[2] * row[3];

// Prefer: named column access
const total = row.get('price') * row.get('quantity');