Map over an object in a preorder or postoder depth-first manner

Overview

obj-walker

Walk objects like this guy.

Walker, Texas Ranger

Map over an object in a preorder or postoder depth-first manner. Also, provides functions for serializing and deserializng self-referential objects using JSON pointer.

This library is designed to work well with functions that traverse an object in the same way JSON.stringify and JSON.parse do. Namely, preorder and postorder. To mimic that behavior entirely set the jsonCompat option to true.

Custom traversal functions are supported. This allows you to walk tree-like structures, such as JSON schema, in a more efficient and logical way. map does not work particularly well for this use case since the path to set values may be incompleted. Prefer walkie in these scenarios instead.

walker

walker(obj: object, walkFn: WalkFn, options: WOptions = {}) => void

Generic walking fn that traverse an object in preorder (default) or postorder, calling walkFn for each node. Can be used directly, but probably shouldn't.

export interface Node {
  key: string | undefined
  val: any
  parents: any[]
  path: string[]
  isLeaf: boolean
  isRoot: boolean
}
import { walker } from 'obj-walker'

const obj = {
  a: {
    b: 23,
    c: 24,
  },
  d: {
    e: 'Bob',
    f: [10, 20, 30],
  },
}

const nodes: Node[] = []
const walkFn = (node: Node) => {
  nodes.push(node)
}
walker(obj, walkFn, options)
nodes

Returns an array of nodes. Note this is how walk works, so prefer that fn.

[
  {
    key: undefined,
    parents: [],
    val: { a: { b: 23, c: 24 }, d: { e: 'Bob', f: [10, 20, 30] } },
    path: [],
    isRoot: true,
    isLeaf: false,
  },
  {
    key: 'a',
    val: { b: 23, c: 24 },
    parents: [{ a: { b: 23, c: 24 }, d: { e: 'Bob', f: [10, 20, 30] } }],
    path: ['a'],
    isLeaf: false,
    isRoot: false,
  },
  {
    key: 'b',
    val: 23,
    parents: [
      { b: 23, c: 24 },
      { a: { b: 23, c: 24 }, d: { e: 'Bob', f: [10, 20, 30] } },
    ],
    path: ['a', 'b'],
    isLeaf: true,
    isRoot: false,
  },
  ...
]

walk

walk(obj: object, options: Options = {}) => Node[]

Walk an object. Returns an array of all nodes in the object in either preorder or postorder.

interface Options {
  postOrder?: boolean
  leavesOnly?: boolean
  jsonCompat?: boolean
  traverse?(x: any): any
}
import { walk } from 'obj-walker'

const obj = {
  a: {
    b: 23,
    c: 24,
  },
  d: {
    e: 'Bob',
    f: [10, 20, 30],
  },
}
walk(obj).map((x) => x.path)

Produces:

[
  [],
  ['a'],
  ['a', 'b'],
  ['a', 'c'],
  ['d'],
  ['d', 'e'],
  ['d', 'f'],
  ['d', 'f', '0'],
  ['d', 'f', '1'],
  ['d', 'f', '2'],
]

walkie

walkie(obj: object, walkFn: WalkFn, options: Options = {}) => object

Walk-each ~ walkie

Walk over an object calling walkFn for each node. The original object is deep-cloned making it possible to simply mutate each node as needed in order to transform the object. The cloned object is returned.

Below I want to walk a MongoDB JSON schema and set additionalProperties to true wherever it exists. I traverse this tree using a custom traverse fn. The original object is not modified.

import { walkie } from 'obj-walker'

const obj = {
  bsonType: 'object',
  additionalProperties: false,
  required: ['name'],
  properties: {
    _id: {
      bsonType: 'objectId',
    },
    name: { bsonType: 'string' },
    addresses: {
      bsonType: 'array',
      items: {
        bsonType: 'object',
        additionalProperties: false,
        properties: {
          address: {
            bsonType: 'object',
            additionalProperties: false,
            properties: {
              zip: { bsonType: 'string' },
              country: { bsonType: 'string' },
            },
          },
        },
      },
    },
  },
}

const traverse = (x: any) => x.properties || (x.items && { items: x.items })
const walkFn = ({ val }: Node) => {
  if (val.hasOwnProperty('additionalProperties')) {
    val.additionalProperties = true
  }
}
walkie(obj, walkFn, { traverse })

Produces:

{
  bsonType: 'object',
  additionalProperties: true,
  required: ['name'],
  properties: {
    _id: { bsonType: 'objectId' },
    name: { bsonType: 'string' },
    addresses: {
      bsonType: 'array',
      items: {
        bsonType: 'object',
        additionalProperties: true,
        properties: {
          address: {
            bsonType: 'object',
            additionalProperties: true,
            properties: {
              zip: { bsonType: 'string' },
              country: { bsonType: 'string' },
            },
          },
        },
      },
    },
  },
}

map

Map over an object modifying values with a fn depth-first in a preorder manner. Exclude nodes by returning undefined. Undefined array values will not be excluded. The output of the mapper fn will be traversed if possible.

map(obj: object, mapper: Mapper, options: MapOptions = {}) => object

Notice the custom traverse fn. This determines how to traverse into an object or array. By default, we only traverse into plain objects and arrays and iterate over there key/value pairs.

import { map } from 'obj-walker'

const obj = {
  a: {
    b: 23,
    c: 24,
  },
  d: {
    e: 'Bob',
    f: [10, null, 30, [31, undefined, 32], 40],
  },
  g: [25, '', { h: [null, 26, 27] }],
  i: 'Frank',
}
map(obj, ({ val }) => (Array.isArray(val) ? _.compact(val) : val))

Produces:

{
  a: { b: 23, c: 24 },
  d: { e: 'Bob', f: [10, 30, [31, 32], 40] },
  g: [25, { h: [26, 27] }],
  i: 'Frank',
}

mapLeaves

mapLeaves(obj: object, mapper: Mapper, options?: Options) => object

Map over the leaves of an object, where a leaf is defined as a value that is not traversable according to either the default traverse fn which traverses plain objects and arrays, or your custom traverse fn.

Exclude nodes by returning undefined. Undefined array values will not be excluded.

mapper is passed a Node object.

import { mapLeaves } from 'obj-walker'

const obj = {
  a: {
    b: 23,
    c: 24,
  },
  d: {
    e: 100,
    f: [10, 20, 30],
  },
}
mapLeaves(obj, ({ val }) => val + 1)

Produces:

{
  a: { b: 24, c: 25 },
  d: { e: 101, f: [11, 21, 31] },
}

addRefs

addRefs(obj: object, options?: RefOptions): object

Replace duplicate objects refs with pointers to the first object seen.

import { addRefs } from 'obj-walker'

const apiOutput = {
  1: 'foo',
  2: 'bar',
  3: 'baz',
}

const detailsOutput = {
  1: 'bla',
  2: 'bla',
  3: 'bla',
}

const obj = {
  api: {
    input: [1, 2, 3],
    output: apiOutput,
  },
  details: {
    input: apiOutput,
    output: detailsOutput,
  },
  writeToDB: {
    input: detailsOutput,
  },
}
addRefs(obj)

Produces:

{
  api: {
    input: [1, 2, 3],
    output: { '1': 'foo', '2': 'bar', '3': 'baz' },
  },
  details: {
    input: { $ref: '#/api/output' },
    output: { '1': 'bla', '2': 'bla', '3': 'bla' },
  },
  writeToDB: { input: { $ref: '#/details/output' } },
}

deref

deref(obj: object, options?: RefOptions): object

Rehydrate objects by replacing refs with actual objects.

import { deref } from 'obj-walker'

const obj = {
  api: {
    input: [1, 2, 3],
    output: { '1': 'foo', '2': 'bar', '3': 'baz' },
  },
  details: {
    input: { $ref: '#/api/output' },
    output: { '1': 'bla', '2': 'bla', '3': 'bla' },
  },
  writeToDB: { input: { $ref: '#/details/output' } },
}
deref(obj)

Produces:

{
  api: {
    input: [1, 2, 3],
    output: { '1': 'foo', '2': 'bar', '3': 'baz' },
  },
  details: {
    input: { '1': 'foo', '2': 'bar', '3': 'baz' },
    output: { '1': 'bla', '2': 'bla', '3': 'bla' },
  },
  writeToDB: { input: { '1': 'bla', '2': 'bla', '3': 'bla' } },
}
You might also like...

A port of bitcoin-core that will (over time) become TS friendly.

bitcoin-core A modern Bitcoin Core REST and RPC client to execute administrative tasks, multiwallet operations and queries about network and the block

Nov 22, 2022

API client to test endpoints over HTTP. Uses superagent under the hood

@japa/client API client to test endpoints over HTTP. Uses superagent under the hood The API client plugin of Japa makes it super simple to test your A

Apr 13, 2022

Non-interactive publicly verifiable distributed key generation and resharing algorithm over BLS12-381

NPVDKG-RS This repository contains a mathematical presentation and some code to demonstrate our developed non-interactive publicly verifiable distribu

May 19, 2022

A meme generator plugin for Figma and FigJam. Import memes from all over the internet with customizable captions and share it far and wide.

A meme generator plugin for Figma and FigJam. Import memes from all over the internet with customizable captions and share it far and wide.

Is This A Meme? 💁🏻 🦋 A meme generator plugin for Figma and FigJam. Import memes from all over the internet, add your captions, and share it far and

Aug 30, 2022

docsQL - Getting an overview over your Markdown file in your Jamstack site

docsQL - Getting an overview over your Markdown file in your Jamstack site

docsQL Getting an overview of your Jamstack Markdown files. Demo Play with: https://peterbe.github.io/docsql/ You're supposed to run docsQL with your

Jan 3, 2023

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols.

A Node.js client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols.

OCPP-RPC A client & server implementation of the WAMP-like RPC-over-websocket system defined in the OCPP-J protcols (e.g. OCPP1.6J and OCPP2.0.1J). Re

Dec 30, 2022

Several custom made and legacy icons, and icons collected all over the internet in 1 set, UI selectable.

Several custom made and legacy icons, and icons collected all over the internet in 1 set, UI selectable.

Custom icon library Several custom made and legacy icons, and icons collected all over the internet in 1 set, UI selectable. Upon each Material Design

Dec 12, 2022

Verify your E-Mail and Phone Number using link sent over mail and sms.

Verify your E-Mail and Phone Number using link sent over mail and sms.

Phone-and-Mail-Verification-With-Link Verify your E-Mail and Phone Number using link sent over mail and sms. Endpoints POST /user/create Body { "n

Sep 14, 2022

Music World is web3 app built over Solana where anyone can add their favourite songs and see the other songs that are added by different people from around the globe.

💥 Introduction Music World is web3 app built over Solana where anyone can add their favourite songs and see the other songs that are added by differe

Jun 10, 2022
Owner
David Sargeant
David Sargeant
Invadium runs exploit playbooks against vulnerable target applications in an intuitive, reproducible, and well-defined manner.

Invadium Invadium runs exploits against one or more target applications in an intuitive, reproducable, and well-defined manner. It focuses on bridging

Dynatrace Open Source 10 Nov 6, 2022
A public board for all the Computer Society and Students to display their profile. An online year-book for you to display your profile in the most creative manner

Student's Yearbook by IEEE Computer Society Student's yearbook is an open-source project which intends to dispaly the students who will be graduating

IEEE Computer Society 11 Dec 18, 2022
An obsidian plugin allowing you to register and view different file extensions in a modular manner.

Obsidian Custom File Extensions Plugin This is a plugin for Obsidian to allow associaton of file type extensions with different in-app views via setti

null 5 Dec 6, 2022
aka Scaletor, take screenshots of a piece of a map and scale/compare with other parts of the map

scale-a-tron A quick-and-dirty map that lets you compare one area to another. Draw a shape around a region, zoom in to another place on the map, and c

Stamen Design 24 Nov 7, 2022
Run RPC over a MessagePort object from a Worker thread (or WebWorker)

thread-rpc Run RPC over a MessagePort object from a Worker thread (or WebWorker) npm install thread-rpc Usage First in the parent thread const Thread

Mathias Buus 9 May 31, 2022
Types generator will help user to create TS types from JSON. Just paste your single object JSON the Types generator will auto-generate the interfaces for you. You can give a name for the root object

Types generator Types generator is a utility tool that will help User to create TS Interfaces from JSON. All you have to do is paste your single objec

Vineeth.TR 16 Dec 6, 2022
⚡ the first open-source redis client made with care and acessibility-first 🚀

⚡ Redis UI The first open-source project to create an awesome and accessible UI for Redis as a native desktop application. ✨ ?? ?? How to develop loca

Nicolas Lopes Aquino 14 Dec 5, 2022
Automating Beef to use over wan without configuring your router

BeefAuto Follow on Social Media Platforms python script Automate Beef And Configure it to use overwan by using ngrok to open ports ScreenShots INSTALL

youhacker55 50 Dec 6, 2022
Make the content slide prettily across the screen with variable sizes of scrolling items, in any of four directions, pausing while the mouse is over the marquee, and all with vanilla JavaScript.

TEG Marquee Make the content slide prettily across the screen with variable sizes of scrolling items, in any of four directions, pausing while the mou

Paul B. Joiner 0 Dec 30, 2021
Chrome extension for granular visual control over Notion.so

Notion Style Tweaks (Beta) A Chrome extension aiming to give granular control over the visual experience of Notion. Written in Svelte. Installation Ch

Eli 47 Dec 25, 2022