Giter Site home page Giter Site logo

yurigor / deepdash Goto Github PK

View Code? Open in Web Editor NEW
270.0 3.0 12.0 8.47 MB

eachDeep, filterDeep, findDeep, someDeep, omitDeep, pickDeep, keysDeep etc.. Tree traversal library written in Underscore/Lodash fashion

Home Page: https://deepdash.io/

License: MIT License

JavaScript 99.77% HTML 0.23%
lodash object traversal tree eachdeep foreachdeep keysdeep paths filterdeep

deepdash's Introduction

Deepdash

eachDeep, filterDeep, findDeep, someDeep, omitDeep, pickDeep, keysDeep etc.. Tree traversal library written in Underscore/Lodash fashion. Standalone or as a Lodash mixin extension

Deepdash lib is used in PlanZed.org - awesome cloud mind map app created by the author of deepdash.
Plz check it, it's free and I need feedback πŸ˜‰

All Contributors Known Vulnerabilities Travis (.org) Coverage Status
NPM

Installation

In a browser

Load script after Lodash, then pass a lodash instance to the deepdash function:

<script src="https://cdn.jsdelivr.net/npm/lodash/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.min.js"></script>
<script>
  deepdash(_);
  console.log(_.eachDeep); // --> new methods mixed into Lodash
</script>

If you don't use Lodash - there is a standalone version:

<script src="https://cdn.jsdelivr.net/npm/deepdash/browser/deepdash.standalone.min.js"></script>
<script>
  console.log(deepdash.eachDeep); // --> all the methods just work
</script>

Standalone Deepdash weighs more then "dry" version, because it includes some of cherry-picked Lodash methods it depends on. But it's better to use Standalone version, than include full Lodash just as dependency, if you don't need Lodash.

Using npm:

npm i --save deepdash

In Node.js:

// load Lodash if you need it
const _ = require('lodash');
//mixin all the methods into Lodash object
require('deepdash')(_);
// or cherry-pick method you only need and mix it into lodash
require('deepdash/addFilterDeep')(_);
// or cherry-pick method separately if you don't want to mutate Lodash instance
const filterDeep = require('deepdash/getFilterDeep')(_);
// If you don't need Lodash - there is standalone version
const deepdash = require('deepdash/standalone'); // full
const filterDeep = require('deepdash/filterDeep'); // or separate standalone methods

There is also deepdash as ES6 module

npm i --save deepdash-es
import lodash from 'lodash-es';
import deepdash from 'deepdash-es';
const _ = deepdash(lodash);

in the ES package there are same cherry-pick and/or standalone methods as in the main package.

import filterDeep from 'deepdash-es/filterDeep';

or

import { filterDeep } from 'deepdash-es/standalone';

or

import _ from 'lodash-es';
import getFilterDeep from 'deepdash-es/getFilterDeep';
const filterDeep = getFilterDeep(_);

or

import _ from 'lodash-es';
import addFilterDeep from 'deepdash-es/addFilterDeep';
addFilterDeep(_);// --> _.filterDeep

Demo

Example react+redux app with nested comments filtered by Deepdash.(source is here)

Methods

eachDeep (forEachDeep)

β€Ί iterate over all the children and sub-children πŸ“š see docs

expand example
let children = [/* expand to see */];
let children = [
  {
    description: 'description for node 1',
    comment: 'comment for node 1',
    note: 'note for node 1',
    name: 'node 1',
    bad: false,
    children: [
      {
        description: 'description for node 1.1',
        comment: 'comment for node 1.1',
        note: 'note for node 1.1',
        name: 'node 1.1',
        bad: false,
      },
      {
        description: 'description for node 1.2',
        comment: 'comment for node 1.2',
        note: 'note for node 1.2',
        name: 'node 1.2',
        good: true,
      },
      {
        description: 'description for node 1.3',
        comment: 'comment for node 1.3',
        note: 'note for node 1.3',
        name: 'node 1.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 2',
    comment: 'comment for node 2',
    note: 'note for node 2',
    name: 'node 2',
    good: true,
    children: [
      {
        description: 'description for node 2.1',
        comment: 'comment for node 2.1',
        note: 'note for node 2.1',
        name: 'node 2.1',
        bad: false,
      },
      {
        description: 'description for node 2.2',
        comment: 'comment for node 2.2',
        note: 'note for node 2.2',
        name: 'node 2.2',
        good: true,
      },
      {
        description: 'description for node 2.3',
        comment: 'comment for node 2.3',
        note: 'note for node 2.3',
        name: 'node 2.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 3',
    comment: 'comment for node 3',
    note: 'note for node 3',
    name: 'node 3',
    bad: true,
    good: false,
    children: [
      {
        description: 'description for node 3.1',
        comment: 'comment for node 3.1',
        note: 'note for node 3.1',
        name: 'node 3.1',
        bad: false,
      },
      {
        description: 'description for node 3.2',
        comment: 'comment for node 3.2',
        note: 'note for node 3.2',
        name: 'node 3.2',
        good: true,
      },
      {
        description: 'description for node 3.3',
        comment: 'comment for node 3.3',
        note: 'note for node 3.3',
        name: 'node 3.3',
        bad: true,
        good: false,
      },
    ],
  },
];
  function displayField(val, key, parent, context) {
      if (_.isArray(parent)) {
        key = '[' + key + ']';
      }
      console.log(
        _.repeat('   ', context.depth) +
          'β†’ ' +
          key +
          ': ' +
          (_.isArray(val)
            ? '[' + val.length + ']'
            : _.isObject(val)
            ? '{' + (val.name || '') + '}'
            : val)
      );
    }

    console.log('\n = Iterate over tree (each child object) = \n');

    _.eachDeep(children, displayField, { childrenPath: 'children' });

    console.log('\n = Iterate over object (each field) = \n');

    _.eachDeep(children, displayField);
Console:
 = Iterate over tree (each child object) =

β†’ [0]: {node 1}
      β†’ [0]: {node 1.1}
      β†’ [1]: {node 1.2}
      β†’ [2]: {node 1.3}
β†’ [1]: {node 2}
      β†’ [0]: {node 2.1}
      β†’ [1]: {node 2.2}
      β†’ [2]: {node 2.3}
β†’ [2]: {node 3}
      β†’ [0]: {node 3.1}
      β†’ [1]: {node 3.2}
      β†’ [2]: {node 3.3}

 = Iterate over object (each field) =

β†’ [0]: {node 1}
   β†’ description: description for node 1
   β†’ comment: comment for node 1
   β†’ note: note for node 1
   β†’ name: node 1
   β†’ bad: false
   β†’ children: [3]
      β†’ [0]: {node 1.1}
         β†’ description: description for node 1.1
         β†’ comment: comment for node 1.1
         β†’ note: note for node 1.1
         β†’ name: node 1.1
         β†’ bad: false
      β†’ [1]: {node 1.2}
         β†’ description: description for node 1.2
         β†’ comment: comment for node 1.2
         β†’ note: note for node 1.2
         β†’ name: node 1.2
         β†’ good: true
      β†’ [2]: {node 1.3}
         β†’ description: description for node 1.3
         β†’ comment: comment for node 1.3
         β†’ note: note for node 1.3
         β†’ name: node 1.3
         β†’ bad: true
         β†’ good: false
β†’ [1]: {node 2}
   β†’ description: description for node 2
   β†’ comment: comment for node 2
   β†’ note: note for node 2
   β†’ name: node 2
   β†’ good: true
   β†’ children: [3]
      β†’ [0]: {node 2.1}
         β†’ description: description for node 2.1
         β†’ comment: comment for node 2.1
         β†’ note: note for node 2.1
         β†’ name: node 2.1
         β†’ bad: false
      β†’ [1]: {node 2.2}
         β†’ description: description for node 2.2
         β†’ comment: comment for node 2.2
         β†’ note: note for node 2.2
         β†’ name: node 2.2
         β†’ good: true
      β†’ [2]: {node 2.3}
         β†’ description: description for node 2.3
         β†’ comment: comment for node 2.3
         β†’ note: note for node 2.3
         β†’ name: node 2.3
         β†’ bad: true
         β†’ good: false
β†’ [2]: {node 3}
   β†’ description: description for node 3
   β†’ comment: comment for node 3
   β†’ note: note for node 3
   β†’ name: node 3
   β†’ bad: true
   β†’ good: false
   β†’ children: [3]
      β†’ [0]: {node 3.1}
         β†’ description: description for node 3.1
         β†’ comment: comment for node 3.1
         β†’ note: note for node 3.1
         β†’ name: node 3.1
         β†’ bad: false
      β†’ [1]: {node 3.2}
         β†’ description: description for node 3.2
         β†’ comment: comment for node 3.2
         β†’ note: note for node 3.2
         β†’ name: node 3.2
         β†’ good: true
      β†’ [2]: {node 3.3}
         β†’ description: description for node 3.3
         β†’ comment: comment for node 3.3
         β†’ note: note for node 3.3
         β†’ name: node 3.3
         β†’ bad: true
         β†’ good: false

Try it yourself β€Ίβ€Ίβ€Ί

filterDeep

β€Ί deep filter object πŸ“š see docs

expand example
let children = [/* expand to see */];
let children = [
  {
    description: 'description for node 1',
    comment: 'comment for node 1',
    note: 'note for node 1',
    name: 'node 1',
    bad: false,
    children: [
      {
        description: 'description for node 1.1',
        comment: 'comment for node 1.1',
        note: 'note for node 1.1',
        name: 'node 1.1',
        bad: false,
      },
      {
        description: 'description for node 1.2',
        comment: 'comment for node 1.2',
        note: 'note for node 1.2',
        name: 'node 1.2',
        good: true,
      },
      {
        description: 'description for node 1.3',
        comment: 'comment for node 1.3',
        note: 'note for node 1.3',
        name: 'node 1.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 2',
    comment: 'comment for node 2',
    note: 'note for node 2',
    name: 'node 2',
    good: true,
    children: [
      {
        description: 'description for node 2.1',
        comment: 'comment for node 2.1',
        note: 'note for node 2.1',
        name: 'node 2.1',
        bad: false,
      },
      {
        description: 'description for node 2.2',
        comment: 'comment for node 2.2',
        note: 'note for node 2.2',
        name: 'node 2.2',
        good: true,
      },
      {
        description: 'description for node 2.3',
        comment: 'comment for node 2.3',
        note: 'note for node 2.3',
        name: 'node 2.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 3',
    comment: 'comment for node 3',
    note: 'note for node 3',
    name: 'node 3',
    bad: true,
    good: false,
    children: [
      {
        description: 'description for node 3.1',
        comment: 'comment for node 3.1',
        note: 'note for node 3.1',
        name: 'node 3.1',
        bad: false,
      },
      {
        description: 'description for node 3.2',
        comment: 'comment for node 3.2',
        note: 'note for node 3.2',
        name: 'node 3.2',
        good: true,
      },
      {
        description: 'description for node 3.3',
        comment: 'comment for node 3.3',
        note: 'note for node 3.3',
        name: 'node 3.3',
        bad: true,
        good: false,
      },
    ],
  },
];
  console.log('\n = Filter tree (good children) = \n');

  console.log(
    _.filterDeep(children, 'good', { childrenPath: 'children' })
  );

  console.log('\n = Filter object (names of good children) = \n');

  console.log(
      _.filterDeep(children, (val, key, parent) => {
        if (key == 'name' && parent.good) return true;
      })
  );
Console:
 = Filter tree (good children) =

[
  {
    "description": "description for node 1",
    "comment": "comment for node 1",
    "note": "note for node 1",
    "name": "node 1",
    "bad": false,
    "children": [
      {
        "description": "description for node 1.2",
        "comment": "comment for node 1.2",
        "note": "note for node 1.2",
        "name": "node 1.2",
        "good": true
      }
    ]
  },
  {
    "description": "description for node 2",
    "comment": "comment for node 2",
    "note": "note for node 2",
    "name": "node 2",
    "good": true,
    "children": [
      {
        "description": "description for node 2.2",
        "comment": "comment for node 2.2",
        "note": "note for node 2.2",
        "name": "node 2.2",
        "good": true
      }
    ]
  },
  {
    "description": "description for node 3",
    "comment": "comment for node 3",
    "note": "note for node 3",
    "name": "node 3",
    "bad": true,
    "good": false,
    "children": [
      {
        "description": "description for node 3.2",
        "comment": "comment for node 3.2",
        "note": "note for node 3.2",
        "name": "node 3.2",
        "good": true
      }
    ]
  }
]

 = Filter object (names of good children) =

[
  {
    "children": [
      {
        "name": "node 1.2"
      }
    ]
  },
  {
    "name": "node 2",
    "children": [
      {
        "name": "node 2.2"
      }
    ]
  },
  {
    "children": [
      {
        "name": "node 3.2"
      }
    ]
  }
]

Try it yourself β€Ίβ€Ίβ€Ί

findDeep

β€Ί find first matching deep meta-value πŸ“š see docs

example a bit later
let children = [/* expand to see */];
// next time
// sorry
Console:
❀️

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

findValueDeep

β€Ί find first matching deep value πŸ“š see docs

example a bit later
let children = [/* expand to see */];
// next time
// sorry
Console:
❀️

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

findPathDeep

β€Ί find the path of the first matching deep value πŸ“š see docs

example a bit later
let children = [/* expand to see */];
// next time
// sorry
Console:
❀️

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

mapDeep

β€Ί get array of values processed by iteratee. πŸ“š see docs

expand example
  let res = _.mapDeep(
    { hello: { from: { the: 'deep world', and: 'deepdash' } } },
    (v) => v.toUpperCase(),
    { leavesOnly: true }
  );
  // res -> ['DEEP WORLD','DEEPDASH']

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

mapValuesDeep

β€Ί get the object with same structure, but transformed values. πŸ“š see docs

expand example
  let res = _.mapValuesDeep(
    { hello: { from: { the: 'deep world' } } },
    (v) => v.toUpperCase(),
    { leavesOnly: true }
  );
  // res -> { hello: { from: { the: 'DEEP WORLD' } } }

Try it yourself β€Ίβ€Ίβ€Ί

mapKeysDeep

β€Ί get the object with same values, but transformed keys. πŸ“š see docs

expand example
  let res = _.mapKeysDeep(
    { hello: { from: { the: 'deep world' } } },
    (v, k) => k.toUpperCase()
  );
  // res -> { HELLO: { FROM: { THE: 'deep world' } } }

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

reduceDeep

β€Ί like reduce, but deep πŸ“š see docs

expand example
  let max = _.reduceDeep({ a: 2, b: 3, c: { d: 6, e: [1, 5, 8] } },
    (acc, value, key, parent, ctx) => {
      if (typeof value == 'number' && (typeof acc != 'number' || value > acc))
        return value;
      return undefined;
    }
  );
  // max == 8

Try it yourself β€Ίβ€Ίβ€Ί

someDeep

β€Ί returns true if some matching deep value found πŸ“š see docs

example a bit later
let children = [/* expand to see */];
// next time
// sorry
Console:
❀️

Try it yourself (no yet) β€Ίβ€Ίβ€Ί

pickDeep

β€Ί pick values by paths specified by endings or regexes πŸ“š see docs

expand example
let children = [/* expand to see */];
let children = [
  {
    description: 'description for node 1',
    comment: 'comment for node 1',
    note: 'note for node 1',
    name: 'node 1',
    bad: false,
    children: [
      {
        description: 'description for node 1.1',
        comment: 'comment for node 1.1',
        note: 'note for node 1.1',
        name: 'node 1.1',
        bad: false,
      },
      {
        description: 'description for node 1.2',
        comment: 'comment for node 1.2',
        note: 'note for node 1.2',
        name: 'node 1.2',
        good: true,
      },
      {
        description: 'description for node 1.3',
        comment: 'comment for node 1.3',
        note: 'note for node 1.3',
        name: 'node 1.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 2',
    comment: 'comment for node 2',
    note: 'note for node 2',
    name: 'node 2',
    good: true,
    children: [
      {
        description: 'description for node 2.1',
        comment: 'comment for node 2.1',
        note: 'note for node 2.1',
        name: 'node 2.1',
        bad: false,
      },
      {
        description: 'description for node 2.2',
        comment: 'comment for node 2.2',
        note: 'note for node 2.2',
        name: 'node 2.2',
        good: true,
      },
      {
        description: 'description for node 2.3',
        comment: 'comment for node 2.3',
        note: 'note for node 2.3',
        name: 'node 2.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 3',
    comment: 'comment for node 3',
    note: 'note for node 3',
    name: 'node 3',
    bad: true,
    good: false,
    children: [
      {
        description: 'description for node 3.1',
        comment: 'comment for node 3.1',
        note: 'note for node 3.1',
        name: 'node 3.1',
        bad: false,
      },
      {
        description: 'description for node 3.2',
        comment: 'comment for node 3.2',
        note: 'note for node 3.2',
        name: 'node 3.2',
        good: true,
      },
      {
        description: 'description for node 3.3',
        comment: 'comment for node 3.3',
        note: 'note for node 3.3',
        name: 'node 3.3',
        bad: true,
        good: false,
      },
    ],
  },
];
  console.log('\n = Pick name and description only = \n');

  console.log(
    _.pickDeep(children, ['name', 'description'])
  );
Console:
 = Pick name and description only =

[
  {
    "description": "description for node 1",
    "name": "node 1",
    "children": [
      {
        "description": "description for node 1.1",
        "name": "node 1.1"
      },
      {
        "description": "description for node 1.2",
        "name": "node 1.2"
      },
      {
        "description": "description for node 1.3",
        "name": "node 1.3"
      }
    ]
  },
  {
    "description": "description for node 2",
    "name": "node 2",
    "children": [
      {
        "description": "description for node 2.1",
        "name": "node 2.1"
      },
      {
        "description": "description for node 2.2",
        "name": "node 2.2"
      },
      {
        "description": "description for node 2.3",
        "name": "node 2.3"
      }
    ]
  },
  {
    "description": "description for node 3",
    "name": "node 3",
    "children": [
      {
        "description": "description for node 3.1",
        "name": "node 3.1"
      },
      {
        "description": "description for node 3.2",
        "name": "node 3.2"
      },
      {
        "description": "description for node 3.3",
        "name": "node 3.3"
      }
    ]
  }
]

Try it yourself β€Ίβ€Ίβ€Ί

omitDeep

β€Ί get object without paths specified by endings or regexes πŸ“š see docs

expand example
let children = [/* expand to see */];
let children = [
  {
    description: 'description for node 1',
    comment: 'comment for node 1',
    note: 'note for node 1',
    name: 'node 1',
    bad: false,
    children: [
      {
        description: 'description for node 1.1',
        comment: 'comment for node 1.1',
        note: 'note for node 1.1',
        name: 'node 1.1',
        bad: false,
      },
      {
        description: 'description for node 1.2',
        comment: 'comment for node 1.2',
        note: 'note for node 1.2',
        name: 'node 1.2',
        good: true,
      },
      {
        description: 'description for node 1.3',
        comment: 'comment for node 1.3',
        note: 'note for node 1.3',
        name: 'node 1.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 2',
    comment: 'comment for node 2',
    note: 'note for node 2',
    name: 'node 2',
    good: true,
    children: [
      {
        description: 'description for node 2.1',
        comment: 'comment for node 2.1',
        note: 'note for node 2.1',
        name: 'node 2.1',
        bad: false,
      },
      {
        description: 'description for node 2.2',
        comment: 'comment for node 2.2',
        note: 'note for node 2.2',
        name: 'node 2.2',
        good: true,
      },
      {
        description: 'description for node 2.3',
        comment: 'comment for node 2.3',
        note: 'note for node 2.3',
        name: 'node 2.3',
        bad: true,
        good: false,
      },
    ],
  },
  {
    description: 'description for node 3',
    comment: 'comment for node 3',
    note: 'note for node 3',
    name: 'node 3',
    bad: true,
    good: false,
    children: [
      {
        description: 'description for node 3.1',
        comment: 'comment for node 3.1',
        note: 'note for node 3.1',
        name: 'node 3.1',
        bad: false,
      },
      {
        description: 'description for node 3.2',
        comment: 'comment for node 3.2',
        note: 'note for node 3.2',
        name: 'node 3.2',
        good: true,
      },
      {
        description: 'description for node 3.3',
        comment: 'comment for node 3.3',
        note: 'note for node 3.3',
        name: 'node 3.3',
        bad: true,
        good: false,
      },
    ],
  },
];
  console.log('\n = Omit paths not ending with "e" = \n');

  console.log(
    _.omitDeep(children, /[^e]$/i, { onMatch: { skipChildren: false } }),
  );
Console:
 = Omit paths not ending with "e" =

[
  {
    "note": "note for node 1",
    "name": "node 1",
    "children": [
      {
        "note": "note for node 1.1",
        "name": "node 1.1"
      },
      {
        "note": "note for node 1.2",
        "name": "node 1.2"
      },
      {
        "note": "note for node 1.3",
        "name": "node 1.3"
      }
    ]
  },
  {
    "note": "note for node 2",
    "name": "node 2",
    "children": [
      {
        "note": "note for node 2.1",
        "name": "node 2.1"
      },
      {
        "note": "note for node 2.2",
        "name": "node 2.2"
      },
      {
        "note": "note for node 2.3",
        "name": "node 2.3"
      }
    ]
  },
  {
    "note": "note for node 3",
    "name": "node 3",
    "children": [
      {
        "note": "note for node 3.1",
        "name": "node 3.1"
      },
      {
        "note": "note for node 3.2",
        "name": "node 3.2"
      },
      {
        "note": "note for node 3.3",
        "name": "node 3.3"
      }
    ]
  }
]

Try it yourself β€Ίβ€Ίβ€Ί

index

β€Ί get an object with all the paths as keys and corresponding values πŸ“š see docs

expand example
  let index = _.index(
    {
      a: {
        b: {
          c: [1, 2, 3],
          'hello world': {},
        },
      },
    },
    { leavesOnly: true }
  );
  console.log(index);

Console:

{ 'a.b.c[0]': 1,
  'a.b.c[1]': 2,
  'a.b.c[2]': 3,
  'a.b["hello world"]': {} }

Try it yourself β€Ίβ€Ίβ€Ί

paths (keysDeep)

β€Ί get an array of paths πŸ“š see docs

expand example
  let paths = _.paths(
    {
      a: {
        b: {
          c: [1, 2, 3],
          'hello world': {},
        },
      },
    },
    { leavesOnly: false }
  );
  console.log(paths);

Console:

[ 'a',
  'a.b',
  'a.b.c',
  'a.b.c[0]',
  'a.b.c[1]',
  'a.b.c[2]',
  'a.b["hello world"]' ]

Try it yourself β€Ίβ€Ίβ€Ί

condense

β€Ί condense sparse array πŸ“š see docs

expand example
  let arr = ['a', 'b', 'c', 'd', 'e'];
  delete arr[1];
  console.log(arr);
  delete arr[3];
  console.log(arr);
  _.condense(arr);
  console.log(arr);

Console:

  [ 'a', <1 empty item>, 'c', 'd', 'e' ]
  [ 'a', <1 empty item>, 'c', <1 empty item>, 'e' ]
  [ 'a', 'c', 'e' ]

Try it yourself β€Ίβ€Ίβ€Ί

condenseDeep

β€Ί condense all the nested arrays πŸ“š see docs

expand example
  let obj = { arr: ['a', 'b', { c: [1, , 2, , 3] }, 'd', 'e'] };
  delete obj.arr[1];
  delete obj.arr[3];
  _.condenseDeep(obj);
  console.log(obj);

Console:

  { arr: [ 'a', { c: [ 1, 2, 3 ] }, 'e' ] }

Try it yourself β€Ίβ€Ίβ€Ί

exists

β€Ί like a _.has but returns false for empty array slots πŸ“š see docs

expand example
  var obj = [, { a: [, 'b'] }];
  console.log(_.exists(obj, 0)); // false
  console.log(_.exists(obj, 1)); // true
  console.log(_.exists(obj, '[1].a[0]')); // false
  console.log(_.exists(obj, '[1].a[1]')); // true

Try it yourself β€Ίβ€Ίβ€Ί

pathToString

β€Ί convert an array to string path (opposite to _.toPath) πŸ“š see docs

expand example
  console.log(_.pathToString(['a', 'b', 'c', 'defg', 0, '1', 2.3]
    ,'prefix1', 'prefix2', '[3]'));
  // prefix1.prefix2[3].a.b.c.defg[0][1]["2.3"]
  console.log(_.pathToString(['"', '"', '"']));
  // ["\\""]["\\""]["\\""]
  console.log(_.pathToString('it.s.a.string'));
  // it.s.a.string

Try it yourself β€Ίβ€Ίβ€Ί

See full docs for details.

Contributors ✨

Thanks goes to these wonderful people (emoji key):


Raz Sinay

πŸ’» πŸ““ πŸ€”

Florent

πŸ› πŸ““

JoeSchr

πŸ€” πŸ““

Matt Black

πŸ€”

Lukas Siemon

πŸ€” πŸ““ πŸ’» πŸ“’ ⚠️

crapthings

πŸ€”

Corrado Masciullo

πŸ› πŸ€”

Jed Richards

πŸš‡

Kolja Zuelsdorf

πŸ› πŸ““ πŸ’‘

Noval Agung Prayogo

πŸ’¬

Nathan Tomsic

πŸ€”

madflow

πŸ’¬

Matthew Kirkley

πŸ› πŸ€”

Torma GΓ‘bor

πŸ› πŸ€” πŸ““

Andreas Richter

πŸ› πŸ““

James

πŸ› πŸ’» πŸ“– πŸ““

rxliuli

πŸ› πŸ’» πŸ“–

TeleMediaCC

πŸ›

Nicolas Coutin

πŸ’΅ πŸ““

barrct

πŸ› πŸ“–

casamia918

πŸ› πŸ’» πŸ“–

ferreirix

πŸ€”

John Camden

πŸ›

Joshua

πŸ’» πŸ“–

This project follows the all-contributors specification. Contributions of any kind welcome!

deepdash's People

Contributors

casamia918 avatar dependabot[bot] avatar joshualcdev avatar raz-sinay avatar rxliuli avatar yurigor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

deepdash's Issues

filterDeep review / refactoring

Same like in filterDeep we have cloneDeep option to let the user specify his own cloning method,
we need also to review all the deepdash methods where flat _.clone method used and also let user replace it.

Type expected

[default] \node_modules\deepdash-es\filterDeep.d.ts:12:5
Type expected.
[default] Checking finished with 1 errors

This seems to be related to the extra pipe before void
) =>
| void
| boolean
| {
skipChildren: boolean;
cloneDeep: boolean;
keepIfEmpty: boolean;
},

There are no errors when changed to
) =>
void
| boolean
| {
skipChildren: boolean;
cloneDeep: boolean;
keepIfEmpty: boolean;
},

Better key matching?

I was thinking about writing a library like this myself :) I'm the author of object-scan and was thinking it might be a great fit to improve key matching and efficiency of this library (I'm not a big fan of regex matching).

Take a look and let me know what you think! It would certainly be more in line with the existing lodash selector syntax.

Would be very happy to help with any questions! Cheers, L~

Problems Uglifying the code

We are seeing code which are incompatible with "use strict"

ERROR in js/vendors~ui.8f578b013cec78bc290e.js from UglifyJs
In strict mode code, functions can only be declared at top level or immediately within another function. [./node_modules/deepdash/private/getIterate.js:101,8][js/vendors~ui.8f578b013cec78bc290e.js:186542,8]

at deepdash/private/getIterate.js:101

      !options['break'] &&
      res !== false &&
      !isCircular &&
      _.isObject(value)
    ) {
      if (options.childrenPath !== undefined) {
        function forChildren(children, cp, scp) { // DECLARING A FUNCTION WITHIN AN EXPRESSION
          if (children && _.isObject(children)) {
            _.forOwn(children, function(childValue, childKey) {
              var childPath = (path || []).concat( (cp || []), [childKey]);
              var strChildPath =
                options.pathFormat == 'array'
                  ? pathToString([childKey], strPath || '', scp || '')
                  : undefined;
              iterate({
                value: childValue,
                callback: callback,

Iterating over inherited props - unexpected breaking change in 5.2

<--- Last few GCs --->

[10820:00000178C3D42B80] 15484 ms: Mark-sweep 1959.0 (2095.8) -> 1959.0 (2095.8) MB, 470.1 / 0.0 ms (average mu = 0.076, current mu = 0.000) allocation failure scavenge might not succeed
[10820:00000178C3D42B80] 15786 ms: Mark-sweep 1959.0 (2095.8) -> 1959.0 (2095.8) MB, 302.8 / 0.0 ms (average mu = 0.044, current mu = 0.000) allocation failure scavenge might not succeed

<--- JS stacktrace --->

==== JS stack trace =========================================

Security context: 0x00789f1408d1
0: builtin exit frame: concat(this=0x036ca3d71271 <JSArray[6552]>,0x019ed21f20a9 <JSArray[1]>,0x019ed21fcb59 <JSArray[0]>,0x036ca3d71271 <JSArray[6552]>)

1: getOwnChildren(aka getOwnChildren) [000002FB79D26571] [P:\DEV\CC\CC\node_modules\deepdash\private\getIterate.js:~211] [pc=0000019BD302EF67](this=0x01909d2804b1 <undefined>,0x02fb79d0bd51 <Object map = 000002306D5E2C19>,0x007db7dff45...

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

mapDeep should return arrray

selectDeep - like findDeep, but returns an array of all the matches, not the first one only.
opposite to filterDeep this method returns just a flat array of chosen meta-values, without preserving the structure of the original data source object.
also shorthands similar to find**:
selectValuesDeep
selectPathsDeep

mapDeep - returns an array of deep values processed by specified iteratee
opposite to mapValuesDeep(gonna be renamed) this method also returns just a flat array of chosen values, without preserving the structure of the original data source object.

omitDeep with function?

Is it possible to use a function in omitDeep? I would like to exclude an item and its children from an array but only if the id of an item matches. Please correct me if this isn't the correct function for this but with filterDeep I got an infinite loop (idk why).

This is what I expect:

omitDeep(
      items,
      (value) => {
          return value.id === itemId
      },
      {
          childrenPath: ['children'],
      }
 )

PS: Thanks for the great lib. This is a huge time saver for me in my current project.

Ability to retain full objects in deepFilter for arrays

deepFilter will 'trim' the objects it filters removing parent keys that don't have children matching your predicate function. I wanted to propose the option to retain the full objects that have children that match your predicate function. So for example

const myList = [ { a: {b: 'hello'}, c: 'world' } ];
const filtered = deepFilter(myList, (value) => { return `${value}`.includes('world') };
// filtered is [ { a: {b: 'hello'}, c: 'world' } ]
// rather than [ {c: 'world' } ];

Separate modules

Hey @YuriGor nice work! Any chance we could export separate modules? Like lodash has:

import _omit from 'lodash/omit'

for smaller bundle size on the browser?

selectDeep

selectDeep - like findDeep, but returns an array of all the matches, not the first one only.
opposite to filterDeep this method returns just a flat array of chosen meta-values, without preserving the structure of the original data source object.
also shorthands similar to find**:
selectValuesDeep
selectPathsDeep

monorepo

  • Consider to use lerna instead of manual package management.
  • check if something can be done for solving npms issue.

Not able to break loop

Trying to break out of loop if key==2 is matched, but it does not work. What would be the right way to break out of loop

_.eachDeep({ id: 1, children: [ {id: 2, children: [ { id: 3, children: []}]}]},(k, v, parent, context) => {
      if(k == 2) {
        return false;
      }
      console.log(k)

    });

ES5 compatibility

Hi,
I'm having an issue when building and running on IE11, a project using deepdash 4.2.10.
The error comes from getHasChildren.js which use an arrow function and cause a 'SCRIPT1002' error in Internet Explorer 11.

Can you fix this issue ?
Thank you !

Research `chain` option of `_.mixin`

Now all the methods mixed into Lodash with default chain=true.
Need to clarify the difference, adjust as needed and write corresponding tests.

Single object root in the 'tree' mode.

Now rootIsChildren option, if set to true, supposes source object is a top-level collection of children.
But no options exist to declare source object as a single root tree item, to make iterator pass it to the iteratee/predicate.
For this case, we have to wrap such an object into an array before iterating, it looks not so good.

includeRoot not working on filterDeep

`const tree = {
id: 1,
rootKey: 'rootVal'
};

let cleaned = _.filterDeep(tree, (k, v) => k === 'rootKey' || v === 'rootVal', {includeRoot: true});

console.log(cleaned);
// get empty object
// expected result: { rootKey: 'rootVal' }`

deepMap can alter structure?

I used deep map to integrate some object node with extra properties and deepMap works perfectly returning new value where I want. The structure of the mapped object remains the same.

Let's suppose I need to transform array of strings node in a leaf with those string concatenated altering the structure:

{ arr: ['a', 'b', { c: ['y', 'w', 'z'] }, 'd', 'e'] }

{ arr: ['a', 'b', { c: "ywz" }, 'd', 'e'] }

I was no able to do that with deepMap. Is it possible?

Iteratee should have less arguments

I received good feedback, I agree with.
Iteratee should have less arguments, now it's hard to remember right order, also code looks unreadable when last argument needed.
value, key, may be source collection will be enough, same as in Lodash.
Rest of arguments such as path, depth, parent* should be passed as fields of single object context or properties.
It will be breaking change, so I need to think twice.

Tree mode optimization

Now in the tree mode deepdash walks over all the nested fields and checks each path of it matches given 'children' criteria, because children path may be configured as a regexp, and it's impossible to say beforehand which nested value is a children collection, until we build and tested it's path.

Regexp support in the 'children' option should be dropped as an overkill. If only constant paths will be allowed, we will be able to go and get children collection directly, without bruteforcing all the possible variants.

Great Refactoring (special Lukas edition)

Ok, it's time to do this:

  • avoid recursion - do everything in the plain loop instead, by pushing and popping current context into some array(=stack) depending on the iteration depth.
  • there is an internal creation of function heavily coupled with surrounding closure - that's also subject of future refactoring.
  • make path format array by default
    (oh noes, breaking changes, v5 to v6 jump without minor versions in between, what a shame)
  • make code readable to let the community contribute easily if anybody would like to
  • Don't forget to measure performance before and after.

getIterate.js performance issues

I've encountered performance issues in my code (which uses filterDeep) and after some research using Chrome DevTools performance tab, it seems that the getIterate.js file causes performance issues.

Here are 2 images displaying the results of the performance test recordings:
image

image

I understand that there must be a deep scan to search for a path which might be relevant for the user (was specified in childrenPath).
but maybe one of these suggestions can help:

  1. Add a maxScanDepth (number) option, which will limit the depth of the search for cases where the user knows his deep object's structure and doesn't want to scan all the paths in the scanned object.
  2. Add a isExactChildrenPath (boolean) for cases where only the paths specified by the user in the childrenPath parameter are relevant and there is no need to search other paths (Ofcourse he must supply the full path).
  3. As mentioned in this stackoverflow question and was tested here
    it is possible to use spread instead of concat for better performance.
    like this:
    currentParents = [...parents, currentObj];
    instead of currentParents = parents.concat(currentObj);
    or
    childPath = [...path, childKey];
    instead of childPath = (path || []).concat([childKey]);

Trees with explicitly children field declared

It's a common case when object has regular structure with predictable children array name.
If it's defined beforehand, we can process nodes rather than separate fields.
This way filtering becomes much easier.
I think I need to implement eachTree and filterTree methods, as a higher level alternatives for eachDeep and filterDeep.
This methods will accept childrenField argument, which can be string path, regex or array of any of this.
Default value will be children

deepdash is not webpackable

I created a simple demo project and included the dist bundle within it to show my problem. I am using deepdash very much as described in the docs, like this:

import _ from 'lodash';
import deepdash from 'deepdash';

deepdash(_);

(reference)

However, webpack seems to have some kind of problem with this, since it creates this line in the bundle:

deepdash__WEBPACK_IMPORTED_MODULE_1___default()(lodash__WEBPACK_IMPORTED_MODULE_0___default.a);

(reference)

which then leads to the following error in the browser:
image

Now I have to put a disclaimer here: I am not entirely sure this is either a deepdash or webpack problem, so I am happy to report it over at webpack if you feel deepdash is doing everything correctly. I am also not sure that I am not just a bit stupid and screw up something myself - in that case, sorry!

I hope the minimal project I set up helps debugging a bit. If I can do anything to help debug, I am happy to do so.

P.S.: This works absolutely fine in node.js where I run all my tests with mocha and such, but webpack just won't take it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.