npm package: @peter.naydenov/walk
Description: Deep copy with fine control during the process. Can be used also as a deep forEach function
Author: Peter Naydenov
Latest version: 5.0.2

# Walk (@peter.naydenov/walk) ( Version 5.x.x )

![version](https://img.shields.io/github/package-json/v/peterNaydenov/walk)
![license](https://img.shields.io/github/license/peterNaydenov/walk)
![npm](https://img.shields.io/npm/dt/%40peter.naydenov/walk)
![GitHub issues](https://img.shields.io/github/issues/peterNaydenov/walk)
![GitHub top language](https://img.shields.io/github/languages/top/peterNaydenov/walk)
![npm package minimized gzipped size (select exports)](https://img.shields.io/bundlejs/size/%40peter.naydenov%2Fwalk)




Creates an immutable copies of javascript data structures(objects, arrays or mixed). Executes callback functions on every object property(object-callback) and every primitive property(key-callback). Callbacks can modify result-object during the walk process. Mask, filter or substitute values during the copy process. 

```js
const result = walk ({
                            data             // (required) Any JS data structure;
                          , objectCallback   // (optional) Function executed on each object property;
                          , keyCallback      // (optional) Function executed on each primitive property;
                    })
// Result will become a exact deep copy of "data" 
// - if callbacks are not defined
// - if callbacks are resolved with "value" without modification
```





## keyCallback
function "**keyCallback**" of the `walk` could be used also as a deep '**forEach**' method no matter of the type of the object(object or array). KeyCallback will be executed on keys that have value type: *string, number, bigint, boolean, symbol, null, undefined, and function*.
Object and arrays will be executed only in objectCallback.

```js
function keyCallbackFn ({value,key,breadcrumbs, IGNORE }) {
    // value: value for the property;
    // key:  key of the property;
    // breadcrumbs: location of the property;
    // IGNORE: constant. Return it if key-value pair should be ignored;
    // Callback should return the value of the property. To ignore property, return constant argument IGNORE
  }

let result = walk ({ data, keyCallback: keyCallbackFn });  // It's the short way to provide only key-callback. Callback functions are optional.
// let result = walk ({ data, keyCallback, objectCallback });  // If both callbacks are available
```


## objectCallback

Optional callback function that is started on each object property including 'root'. Function should return object or will be ignored in copy process.

```js
function objectCallbackFn ({ value, key, breadcrumbs, IGNORE }) {
      // value: each object during the walk
      // key: key of the object
      // breadcrumbs: location of the object
      // IGNORE: Constant. Return it if key-value pair should be ignored;
      // object callback should return something.
}

let result = walk ({ data, keyCallback:keyCallbackFn, objectCallback : objectCallbackFn })
```

**IMPORTANT: Object-callbacks are executed always before key-callbacks. If we have both callbacks, then key-callbacks will be executed on the result of object-callback.**

Skip key-callbacks by not defining them:
```js
 let result = walk ({ data, objectCallback: objectCallbackFn })   // ignore keyCallback
```


## Installation

Install for node.js projects by writing in your terminal:

```
npm install @peter.naydenov/walk
```

Once it has been installed, it can be used by writing this line of JavaScript:
```js
import walk from '@peter.naydenov/walk'
```



## How to use it

### Deep copy
```js
let myCopy = walk ({ data:x })   // where x is some javascript data structure
```



### Deep 'forEach'
```js
let x = {
          ls    : [ 1,2,3 ]
        , name  : 'Peter'
        , props : {
                      eyeColor: 'blue'
                    , age     : 47
                    , height  : 176
                    , sizes : [12,33,12,21]
                }
    };

walk ({ data:x, keyCallback : ({value,key, breadcrumbs}) => {
                  console.log (`${key} ----> ${value}`)   // Show each each primitive couples key->value
                  console.log ( `Property location >> ${breadcrumbs}`)
                  // example for breadcrumbs: 'age' will looks like this : 'root/props/age'
              }
    })
```


### Ignore a key

```js
let x = {
          ls    : [ 1,2,3 ]
        , name  : 'Peter'
        , props : {
                      eyeColor: 'blue'
                    , age     : 47
                    , height  : 176
                    , sizes : [12,33,12,21]
                }
    };
let result = walk ({ data:x, keyCallback : ({ value, key, IGNORE }) => {
                        if ( key === 'name' )   return IGNORE
                        return value
                })
// result will copy all properties from x without the property 'name'.
// result.name === undefined
```


### Mask values

```js
let x = {
          ls    : [ 1,2,3 ]
        , name  : 'Peter'
        , props : {
                      eyeColor: 'blue'
                    , age     : 47
                    , height  : 176
                    , sizes : [12,33,12,21]
                }
    };
let result = walk ({ data:x, keyCallback : () => 'xxx' })
// 'result' will have the same structure as 'x' but all values are 'xxx'
// {
//      ls    : [ 'xxx','xxx','xxx' ]
//    , name  : 'xxx'
//    , props : {
//                  eyeColor: 'xxx'
//                , age     : 'xxx'
//                , height  : 'xxx'
//                , sizes : ['xxx','xxx','xxx','xxx']
//             }
//   } 
```

### Change object on condition

```js
let x = {
          ls    : [ 1,2,3 ]
        , name  : 'Peter'
        , props : {
                      eyeColor: 'blue'
                    , age     : 48
                    , height  : 176
                    , sizes : [12,33,12,21]
                }
    };

function objectCallback ({ value:obj, key, breadcrumbs }) {
    if ( key === 'root' ) return obj   // Add this row to ignore 'root' object
    const {age, height} = obj;
    if ( age && age > 30 ) {
            return { age, height }
        }
    return obj
}

let result = walk ({ data:x, objectCallback })
// 'result.props' will have only 'age' and 'height' properties.
// {
//      ls    : [ 1,2,3 ]
//    , name  : 'Peter'
//    , props : {
//                  age     : 48
//                , height  : 176
//             }
//   } 
```

## Limitations
- `walk` keyCallback can return only primitives;
- `walk` can not execute another `walk` from inside of the callbacks;
- It's not recomended to use any async operations in the callbacks. Could compromise the result without any warning;

These limitations are covered in a bit larger library - [walk-async](https://github.com/PeterNaydenov/walk-async). Interface is very simular but result is coming as a promise and callbacks should be resolved or rejected.

## Links
- [Release history](Changelog.md)
- [ Walk-async library](https://github.com/PeterNaydenov/walk-async)
- [ Documentation version 4.x.x](https://github.com/PeterNaydenov/walk/blob/master/README_v.4.x.x.md)
- [ Migration guide ](https://github.com/PeterNaydenov/walk/blob/master/Migration.guide.md)



## Credits
'@peter.naydenov/walk' was created and supported by Peter Naydenov.

## License
'@peter.naydenov/walk' is released under the MIT License.