Skip to main content

Built and signed on GitHub Actions

Dot notation for working with deeply nested complex data (with * wild cards)

This package works with DenoIt is unknown whether this package works with Cloudflare Workers, Node.js, Bun, Browsers
It is unknown whether this package works with Cloudflare Workers
It is unknown whether this package works with Node.js
This package works with Deno
It is unknown whether this package works with Bun
It is unknown whether this package works with Browsers
JSR Score
100%
Published
a month ago (0.2.0)

DotDotData

Complex, deeply nested data is painful. Let's simplify it! ✅ wildcard(*) supported dot notation for accessing complex deeply nested data via data_get, data_set, data_fill, & data_forget

tap for side effects

retry for async data fetching

value for conditional function execution

filled for checking if a value is not blank or empty

blank for checking if a value is considered blank or empty

throw_if and throw_unless for cleaning up try/catch blocks

optional for safe access to potentially null or undefined objects

transform for transforming a value if it's not blank, otherwise returns a default value

Usage

const data = {
  family: {
    children: [{ name: "Sarah" }, { name: "Tom" }, { name: "Anthony" }]
  }
};

data_get("family.children.1.name", data) // "Tom"
data_get("family.children.*.name", data) // ["Sarah", "Tom", "Anthony"]

data_set("family.children.1.name", "Tommy", data) 
data_get("family.children.1.name", data) // "Tommy" 

data_get("family.children.*", data) 
// [{ name: "Sarah" }, { name: "Tommy" }, { name: "Anthony" }]

data_fill("family.children.1.name", "Tommy", data) // ["Sarah", "Tommy", "Anthony"]

data_fill("family.children.3.name", "Andrew", data)
data_get("family.children", data) // // [{ name: "Sarah" }, { name: "Tommy" }, { name: "Anthony" }, { name: "Andrew" }]

data_get("family.children.name.*", data) // ["Sarah", "Tommy", "Anthony", "Andrew"]

data_forget("family.children.1", data)
data_get("family.children", data) // [{ name: "Sarah" }, { name: "Anthony" }, { name: "Andrew" }]

data_forget("family.children.*", data)
data_get("family.children", data) // []

Table of Contents

Helper Functions

data_get

Safely retrieves a value from a nested object or array using dot notation.

import { data_get } from "@findhow/dotdotdata";

const data = {
  user: {
    profile: {
      name: "John Doe",
      address: {
        city: "New York"
      }
    },
    posts: [
      { title: "First Post", comments: 5 },
      { title: "Second Post", comments: 3 }
    ]
  }
};

console.log(data_get(data, 'user.profile.name')); // "John Doe"
console.log(data_get(data, 'user.posts.1.title')); // "Second Post"
console.log(data_get(data, 'user.settings.theme', 'default')); // "default"
console.log(data_get(data, 'user.posts.*.comments')); // [5, 3]

data_set

Sets a value in a nested object or array using dot notation, creating intermediate objects/arrays as needed.

import { data_set } from "@findhow/dotdotdata";

let user = {};

data_set(user, 'profile.name', 'Jane Doe');
data_set(user, 'settings.notifications.email', true);
data_set(user, 'posts.0.title', 'My First Post');

console.log(user);
// {
//   profile: { name: 'Jane Doe' },
//   settings: { notifications: { email: true } },
//   posts: [{ title: 'My First Post' }]
// }

data_fill

Fills a value in an object if it doesn't exist, without overwriting existing values.

import { data_fill } from "@findhow/dotdotdata";

let config = {
  app: {
    name: "MyApp",
    version: "1.0.0"
  }
};

data_fill(config, 'app.debug', true);
data_fill(config, 'app.name', 'NewName'); // Won't overwrite existing value
data_fill(config, 'database.host', 'localhost');

console.log(config);
// {
//   app: {
//     name: "MyApp",
//     version: "1.0.0",
//     debug: true
//   },
//   database: {
//     host: "localhost"
//   }
// }

data_forget

Removes an item from an array or object using dot notation.

import { data_forget } from "@findhow/dotdotdata";

let data = {
  user: {
    profile: { name: "John", age: 30 },
    posts: [
      { id: 1, title: "Post 1" },
      { id: 2, title: "Post 2" }
    ]
  }
};

data_forget(data, 'user.profile.age');
data_forget(data, 'user.posts.0');

console.log(data);
// {
//   user: {
//     profile: { name: "John" },
//     posts: [{ id: 2, title: "Post 2" }]
//   }
// }

optional

Provides safe access to properties of potentially null or undefined objects.

import { optional } from "@findhow/dotdotdata";

const user = null;
console.log(optional(user)?.name?.toUpperCase()); // undefined (no error thrown)

const validUser = { name: "Alice", getAge: () => 25 };
console.log(optional(validUser).name); // "Alice"
console.log(optional(validUser).getAge?.()); // 25

blank

Checks if a value is considered blank or empty.

import { blank } from "@findhow/dotdotdata";

console.log(blank("")); // true
console.log(blank(null)); // true
console.log(blank(undefined)); // true
console.log(blank([])); // true
console.log(blank({})); // true
console.log(blank(0)); // false
console.log(blank(false)); // false

filled

Checks if a value is not blank or empty.

import { filled } from "@findhow/dotdotdata";

console.log(filled("Hello")); // true
console.log(filled([1, 2, 3])); // true
console.log(filled({ key: "value" })); // true
console.log(filled("")); // false
console.log(filled(null)); // false

transform

Transforms a value if it's not blank, otherwise returns a default value.

import { transform } from "@findhow/dotdotdata";

const result1 = transform("hello", (str) => str.toUpperCase());
console.log(result1); // "HELLO"

const result2 = transform("", (str) => str.toUpperCase(), "DEFAULT");
console.log(result2); // "DEFAULT"

const result3 = transform(null, (val) => val * 2, () => "COMPUTED DEFAULT");
console.log(result3); // "COMPUTED DEFAULT"

retry

Attempts to execute a callback until it succeeds or the maximum attempt count is reached.

import { retry } from "@findhow/dotdotdata";

const fetchData = async () => {
  const response = await fetch('https://api.example.com/data');
  if (!response.ok) throw new Error('Failed to fetch');
  return response.json();
};

try {
  const data = await retry(fetchData, 3, 1000);
  console.log(data);
} catch (error) {
  console.error("All attempts failed:", error);
}

tap

Passes the value to a callback function and returns the original value, useful for performing side effects.

import { tap } from "@findhow/dotdotdata";

const result = [1, 2, 3, 4, 5]
  .map(n => n * 2)
  .tap(arr => console.log("Doubled array:", arr))
  .filter(n => n > 5);

console.log(result); // [6, 8, 10]

throw_if and throw_unless

Throw exceptions based on conditions.

import { throw_if, throw_unless } from "@findhow/dotdotdata";

function processAge(age: number) {
  throw_if(age < 0, Error, "Age cannot be negative");
  throw_unless(age >= 18, Error, "Must be at least 18 years old");
  console.log("Age processed successfully");
}

try {
  processAge(20); // Age processed successfully
  processAge(15); // Throws: Error: Must be at least 18 years old
  processAge(-5); // Throws: Error: Age cannot be negative
} catch (error) {
  console.error(error);
}

value

Returns the value if it's not a function, otherwise calls the function and returns its result.

import { value } from "@findhow/dotdotdata";

console.log(value(5)); // 5
console.log(value(() => 10)); // 10

const config = {
  debug: true,
  logLevel: () => process.env.LOG_LEVEL || 'info'
};

console.log(value(config.debug)); // true
console.log(value(config.logLevel)); // 'info' (assuming process.env.LOG_LEVEL is not set)

Advanced Usage Examples

Comprehensive Example: Managing a Library System

Let's walk through a scenario of managing a library system using our helper functions. This example will demonstrate how these functions can simplify complex data operations.

import { data_get, data_set, optional, transform } from "@findhow/dotdotdata";

const complexData = {
  users: [
    { id: 1, name: "Alice", details: { age: 30, city: "New York" } },
    { id: 2, name: "Bob", details: null },
    { id: 3, name: "Charlie" }
  ],
  settings: {
    theme: "dark",
    notifications: {
      email: true,
      push: false
    }
  }
};

// Safely access nested properties
const aliceCity = data_get(complexData, 'users.0.details.city', 'Unknown');
console.log(aliceCity); // "New York"

const bobAge = data_get(complexData, 'users.1.details.age', 'N/A');
console.log(bobAge); // "N/A"

// Use optional chaining for nullable objects
const bobDetails = optional(complexData.users[1].details);
console.log(bobDetails?.age); // undefined (no error thrown)

// Transform data safely
const userCities = complexData.users.map(user => 
  transform(user.details, details => details.city, 'Unknown')
);
console.log(userCities); // ["New York", "Unknown", "Unknown"]

// Modify nested data
data_set(complexData, 'users.1.details', { age: 25, city: "London" });
data_set(complexData, 'settings.notifications.sms', true);

console.log(complexData.users[1].details); // { age: 25, city: "London" }
console.log(complexData.settings.notifications); // { email: true, push: false, sms: true }

Simple Examples

data_get

import { data_get } from "@findhow/dotdotdata";

const data = {
  user: {
    profile: {
      name: "John Doe",
      address: {
        city: "New York"
      }
    },
    posts: [
      { title: "First Post", comments: 5 },
      { title: "Second Post", comments: 3 }
    ]
  }
};

data_get(data, 'user.profile.name'); // "John Doe"
data_get(data, 'user.posts.1.title'); // "Second Post"
data_get(data, 'user.settings.theme', 'default'); // "default"
data_get(data, 'user.posts.*.title'); // ["First Post", "Second Post"]

data_set

import { data_set } from "@findhow/dotdotdata";

let user = {};

data_set(user, 'profile.name', 'Jane Doe');
data_set(user, 'settings.notifications.email', true);
data_set(user, 'posts.0.title', 'My First Post');
data_set(user, 'posts.1.title', 'My Second Post');
console.log(user);
// {
//   profile: { name: 'Jane Doe' },
//   settings: { notifications: { email: true } },
//   posts: [{ title: 'My First Post' }, { title: 'My Second Post' }]
// }

// data_set wildcard
data_set(user, 'posts.*.title', 'Wildcard Post');
console.log(user); 
// {
//   profile: { name: 'Jane Doe' },
//   settings: { notifications: { email: true } },
//   posts: [{ title: 'Wildcard Post' }, { title: 'Wildcard Post' }]
// }

data_fill

import { data_fill } from "@findhow/dotdotdata"; 

let config = {
  app: {
    name: "MyApp",
    version: "1.0.0"
  }
};

data_fill(config, 'app.debug', true);
data_fill(config, 'app.name', 'NewName'); // Won't overwrite existing value
data_fill(config, 'database.host', 'localhost');

console.log(config);
// {
//   app: {
//     name: "MyApp",
//     version: "1.0.0",
//     debug: true
//   },
//   database: {
//     host: "localhost"
//   }
// }

data_forget

import { data_forget } from "@findhow/dotdotdata";

let data = {
  user: {
    profile: { name: "John", age: 30 },
    posts: [
      { id: 1, title: "Post 1" },
      { id: 2, title: "Post 2" }
    ]
  }
};

data_forget(data, 'user.profile.age');
data_forget(data, 'user.posts.0');
console.log(data);
// {
//   user: {
//     profile: { name: "John" },
//     posts: [{ id: 2, title: "Post 2" }]
//   }
// }

optional

import { optional } from "@findhow/dotdotdata";

const user = null;
console.log(optional(user)?.name?.toUpperCase()); // undefined (no error thrown)

const validUser = { name: "Alice", getAge: () => 25 };
console.log(optional(validUser).name); // "Alice"
console.log(optional(validUser).getAge?.()); // 25
Built and signed on
GitHub Actions
View transparency log

Add Package

deno add jsr:@findhow/dotdotdata

Import symbol

import * as mod from "@findhow/dotdotdata";

---- OR ----

Import directly with a jsr specifier

import * as mod from "jsr:@findhow/dotdotdata";

Add Package

npx jsr add @findhow/dotdotdata

Import symbol

import * as mod from "@findhow/dotdotdata";

Add Package

yarn dlx jsr add @findhow/dotdotdata

Import symbol

import * as mod from "@findhow/dotdotdata";

Add Package

pnpm dlx jsr add @findhow/dotdotdata

Import symbol

import * as mod from "@findhow/dotdotdata";

Add Package

bunx jsr add @findhow/dotdotdata

Import symbol

import * as mod from "@findhow/dotdotdata";