Skip to content
Janry edited this page Dec 19, 2016 · 10 revisions

Iterator (not recursive)

Simple iterator based on {RecursiveIterator} instead forEach method.

Description

import RecursiveIterator from 'your_path';

class Iterator extends RecursiveIterator {
    /**
     * @param {Object|Array} iterated
     */
    constructor(iterated) {
        super(iterated, 1, false, 1);
    }
}

export default Iterator;

Usage

import Iterator from 'your_path';

for(let {node, key} of new Iterator([1, 2, 3])) {
    console.log(node);
}

for(let {node, key} of new Iterator(document.getElementsByTagName('h1'))) {
    console.log(node, key);
}

DomIterator

Example of DOM iterator.

Description

import RecursiveIterator from 'your_path';

class DomIterator extends RecursiveIterator {
    /**
     * @param {*} arguments_
     */
    constructor(...arguments_) {
        super(...arguments_);
    }
    /**
     * @param {*} any
     * @returns {Boolean}
     */
    isLeaf(any) {
        return super.isLeaf(any) && !any.children.length;
    }
    /**
     * Returns states of child nodes
     * @param {Object} node
     * @param {Array} path
     * @param {Number} deep
     * @returns {Array<Object>}
     */
    getStatesOfChildNodes(node, path, deep) {
        return Array.from(node.children).map((value) =>
            this.getState(node, value, value.tagName, path.concat(value.tagName), deep + 1)
        );
    }
}

/**
 * @param {*} any
 * @returns {Boolean}
 */
function isObject(any) {
    return any !== null && typeof any === 'object';
}

export default DomIterator;

Usage

<ul id="root">
    <li>1
        <ul>
            <li>1.1</li>
            <li>1.2</li>
        </ul>
    </li>
    <li>2</li>
</ul>
import DomIterator from 'your_path';

var root = document.getElementById('root');

for(let {node, path} of new DomIterator(root)) {
    console.log(path.join('.'), node);
}

// LI    <li>​…​</li>​
// LI.UL    <ul>​…​</ul>​
// LI.UL.LI    <li>​1.1​</li>​
// LI.UL.LI    <li>​1.2​</li>​
// LI    <li>​2​</li>​

Deep copy / Deep clone

var toString = Object.prototype.toString;

/**
 * @param {*} any
 * @returns {Boolean}
 */
function isObject(any) {
    return any !== null && typeof any === 'object';
}

/**
 * @param {*} any
 * @returns {String}
 */
function getType(any) {
    return toString.call(any).slice(8, -1);
}

/**
 * @param {*} any
 * @returns {*}
 */
function shallowCopy(any) {
    var type = getType(any);
    switch (type) {
        case 'Object':
            return {};
        case 'Array':
            return [];
        case 'Date':
            return new Date(any);
        case 'RegExp':
            return new RegExp(any);
        case 'Number':
        case 'String':
        case 'Boolean':
        case 'Undefined':
        case 'Null':
            return any;
        default:
            return String(any);
    }
}

/**
 * @param {*} any
 * @param {Boolean} [deep]
 * @returns {*}
 */
function copy(any, deep = false) {
    if (!deep || !isObject(any)) {
        return shallowCopy(any);
    }

    var map = new Map();
    var rootNode = shallowCopy(any);
    map.set(any, rootNode);

    for(var {parent, node, key} of new RecursiveIterator(any, 1, true)) {
        var parentNode = map.get(parent);
        var cloneNode = shallowCopy(node);
        parentNode[key] = cloneNode;
        map.set(node, cloneNode);
    }

    map.clear();

    return rootNode;
}

// ---------------------------------
// USAGE
// ---------------------------------

var some = {
    foo: {
        bar: 1
    }
};

var clone = copy(some, true);
alert(JSON.stringify(clone));
alert(clone === some); // false

To Query String

let {RecursiveIterator, encodeURIComponent} = window;
let {isArray, isObject} = angular; // (!) YOU MUST IMPLEMENT THESE FUNCTIONS

/**
 * Converts path to name
 * @param {Array} path
 * @returns {String}
 */
function toName(path) {
    let array = path.map(part => `[${encodeURIComponent(part)}]`);
    array[0] = path[0];
    return array.join('');
}


/**
 * @param {Object|Array} any
 * @returns {String}
 */
export default function toQueryString(any) {
    if (!isObject(any) && !isArray(any)) {
        throw new TypeError('Argument must be object or array');
    }

    let stack = [];

    for(let {node, path} of new RecursiveIterator(any)) {
        if (isObject(node)) continue;
        let name = toName(path);
        let value = encodeURIComponent(node);
        stack.push(`${name}=${value}`);
    }

    return stack.join('&');
}

To Form Data

'use strict';

let {FormData} = window;
let {toString} = Object.prototype;
let {isObject, isUndefined} = angular; // (!) YOU MUST IMPLEMENT THESE FUNCTIONS

/**
 * Returns type of anything
 * @param {Object} any
 * @returns {String}
 */
function getType(any) {
    return toString.call(any).slice(8, -1);
}
/**
 * Converts path to FormData name
 * @param {Array} path
 * @returns {String}
 */
function toName(path) {
    let array = path.map((part) => `[${part}]`);
    array[0] = path[0];
    return array.join('');
}

/**
 * Converts object to FormData
 * @param {Object} object
 * @returns {FormData}
 */
export default function(object) {
    if (!isObject(object)) {
        throw new TypeError('Argument must be object');
    }

    let form = new FormData();
    let iterator = new RecursiveIterator(object, 0, true);

    let appendToForm = function(path, node, filename) {
        let name = toName(path);
        if (isUndefined(filename)) {
            form.append(name, node);
        } else {
            form.append(name, node, filename);
        }
    };

    iterator.onStepInto = function({parent, node}) {
        let type = getType(node);
        switch (type) {
            case 'Array':
                return true; // step into
            case 'Object':
                return true; // step into
            case 'FileList':
                return true; // step into
            default:
                return false; // prevent step into
        }
    };

    for(let {node, path} of iterator) {
        var type = getType(node);
        switch (type) {
            case 'Array':
                break;
            case 'Object':
                break;
            case 'FileList':
                break;
            case 'File':
                appendToForm(path, node);
                break;
            case 'Blob':
                appendToForm(path, node, node.name);
                break;
            default:
                appendToForm(path, node);
                break;
        }
    }

    return form;
}

Uninformed search algorithms

Documentation EN, RU.

Breadth-first search (BFS)

new RecursiveIterator(root, 1);

See options bypassMode.

Depth-First Search (DFS)

new RecursiveIterator(root, 0);
// or new RecursiveIterator(root);

See options bypassMode.

Depth-limited search (DLS)

new RecursiveIterator(root, 0, false, /* limit */);

See options maxDeep.

Uniform-Cost Search (UCS)

// sample of node
export default class Node {
    /**
     * @param {Number} cost
     * @param {Array<Node>} [children]
     */
    constructor(cost = 0, children = []) {
        this.cost = cost;
        this.children = children;
    }
}
import RecursiveIterator from 'your_path';

export default class UniformCostIterator extends RecursiveIterator {
    /**
     * @param {Object|Array} root
     * @param {Boolean} [ignoreCircular=false]
     * @param {Number} [maxDeep=100]
     */
    constructor(root, ignoreCircular, maxDeep) {
        super(root, 0, ignoreCircular, maxDeep);
    }
    /**
     * Returns states of child nodes
     * @param {Object} node
     * @param {Array} path
     * @param {Number} deep
     * @returns {Array<Object>}
     */
    getStatesOfChildNodes(node, path, deep) {
        return node.children
            .map((child, key) =>
                this.getState(node, child, key, path.concat(child.cost), deep + 1))
            .sort((a, b) =>
                a.node.cost - b.node.cost);
    }
}
// usage
import UniformCostIterator from './UniformCostIterator';
import Node from  './Node';

var root = new Node(0, [
    new Node(2, [
        new Node(8), new Node(4)
    ]),
    new Node(1, [
        new Node(12, [
            new Node(4)
        ]),
        new Node(9, [
            new Node(7, [
                new Node(6)
            ]),
            new Node(5, [
                new Node(10)
            ])
        ])
    ]),
    new Node(3)
]);

for(var {node, path} of new UniformCostIterator(root)) {
    console.log(node, path.join('.'));
}

// Node {cost: 1, children: Array[2]} "1"
// Node {cost: 9, children: Array[2]} "1.9"
// Node {cost: 5, children: Array[1]} "1.9.5"
// Node {cost: 10, children: Array[0]} "1.9.5.10"
// Node {cost: 7, children: Array[1]} "1.9.7"
// Node {cost: 6, children: Array[0]} "1.9.7.6"
// Node {cost: 12, children: Array[1]} "1.12"
// Node {cost: 4, children: Array[0]} "1.12.4"
// Node {cost: 2, children: Array[2]} "2"
// Node {cost: 4, children: Array[0]} "2.4"
// Node {cost: 8, children: Array[0]} "2.8"
// Node {cost: 3, children: Array[0]} "3"