Skip to content

KoichiKiyokawa/smart-mole

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

smart-mole

npm version GitHub issues GitHub license

Introduction

JavaScript library to dig nested object returned from api.

If api endpoint returns,

{
  animal: {
    mammal: {
      moles: [
        {
          name: 'parent1',
          children: [{ name: 'child1-1' }, { name: 'child1-2' }]
         },
        {
          name: 'parent2',
          children: [{ name: 'child2-1' }, { name: 'child2-2' }]
        }
      ]
    }
  }
}

and you want to get ['child1-2', 'child1-2', 'child2-1', 'child2-2'], with pure javascript,

object.animal.mammal.moles.map(mole => mole.children.map(child => child.name)).flat()
// or
object.animal.mammal.moles.flatMap(mole => mole.children.map(child => child.name))
// => ['child1-2', 'child1-2', 'child2-1', 'child2-2']

This code does not show the composition of the object, so it is difficult to understand how is the object.

On the other hand, with this library,

import { dig, $$target, $$array } from 'smart-mole'

dig(obj, {
  animal: {
    mammal: {
      moles: $$array({
        children: $$array({ name: $$target })
      })
    }
  }
})
// => ['child1-2', 'child1-2', 'child2-1', 'child2-2']

Quick start

Install

npm i smart-mole

or

yarn add smart-mole

How to use

import { dig } from smart-mole

dig(<target_object>, <target_map>, <target_marker>)
argument description
target_object object or array you want to dig
target_map object or array presenting target location, by marking target with target_marker
target_marker optional, default is $$target which is a constant of **target**

Note that target_marker should NOT be included in any keys of <target_object>. if included, it will cause a bug. Bad example:

const object = { '**target**': { bar: 1 } }
dig(object, { '**target**': { bar: $$target } }

You should change target_marker like this.

dig(object, { '**target**': { bar: '***' } }, '***')

Algorithm

  1. Convert target_map to JSON string.
JSON.stringify(target_map)
// => {"animal":{"mammal":{"moles":[{"name":"_"},{"name":"*"}]}}}
  1. From the result of 1, search a object or array that includes target_marker.
{"animal":{"mammal":{"moles":[{"name":"_"},{"name":"*"}]}}}
//                                         ^^^^^^^^^^^^
=> {"name":"*"}
  1. From the result of 2. get the key and memorize it.
{"name":"*"}
=> 'name'
// keys <- 'name'
  1. replace the result of 2. by target_marker.
{"animal":{"mammal":{"moles":[{"name":"_"},{"name":"*"}]}}}
//                                         ^^^^^^^^^^^^
// replace => {"animal":{"mammal":{"moles":[{"name":"_"},"*"]}}}
  1. From the result of 4, search a object or array that includes target_marker(like 2.)
{"animal":{"mammal":{"moles":[{"name":"_"},"*"]}}}
//                           ^^^^^^^^^^^^^^^^^^
  1. get the key(or index) and memorize it.
[{"name":"_"},"*"]
=> 1 (index)
// keys <- 1
// keys: ['name', 1]
  1. repeat until the result of replacement is same as target_marker.
keys: ['name', 1, 'moles', 'mammal', 'animal']
  1. Using keys, dig a target object
let value = object
for (const key of keys.reverse()) {
 value = value[key]
}
value // => 'Don Resetti' (same as object.animal.mammal.moles[1].name)

Caution

You should just use Destructuring assignment in some case.

/*
object is {
  animal: {
    mamal: {
      moles: [
        { name: 'Mr. Resetti' },
        { name: 'Don Resetti' }
      ]
    }
  }
}
*/
// you can get 'Don Resetti' by the folloing method
const { animal: { mamal: { moles: [, { name }] } } } = object
console.log(name) // => 'Don Resetti'

License

MIT