<commit_before>/*
 * Waltz - Enterprise Architecture
 * Copyright (C) 2016  Khartec Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import _ from "lodash";
import {nest} from "d3-collection";
import {checkIsEntityRef} from "./checks";


export function notEmpty(xs) {
    return ! _.isEmpty(xs);
}


export function isEmpty(xs) {
    return _.isEmpty(xs);
}


export function mkSafe(xs = []) {
    return _.isEmpty(xs) ? [] : xs;
}


/**
 * Takes an array and groups by a key extraction function, processing values
 * with a value transformation function.
 * @param coll
 * @param keyFn
 * @param valFn
 */
export function groupAndMap(coll = [], keyFn = d => d.id, valFn = d => d) {
    return _.reduce(
        coll,
        (acc, x) => {
            const k = keyFn(x);
            const bucket = acc[k] || [];
            bucket.push(valFn(x));
            acc[k] = bucket;
            return acc;
        },
        {});
}

/**
 * Given data that looks like:
 *
 *    [ { id: "",  parentId: ?, ... } , .. ]
 *
 * Gives back an array of top level objects which have children
 * nested in them, the result looks something like:
 *
 *    [ id: "", parentId : ?, parent : {}?, children : [ .. ], ... },  .. ]
 *
 * @param nodes
 * @returns {Array}
 */
export function populateParents(nodes) {
    const byId = _.chain(_.cloneDeep(nodes))
        .map(u => _.merge(u, { children: [], parent: null }))
        .keyBy('id')
        .value();

    _.each(_.values(byId), u => {
        if (u.parentId) {
            const parent = byId[u.parentId];
            if (parent) {
                parent.children.push(u);
                u.parent = parent;
            }
        }
    });

    return _.values(byId);
}


export function buildHierarchies(nodes) {
    // only give back root element/s
    return _.reject(populateParents(nodes), n => n.parent);
}


export function findNode(nodes = [], id) {
    const found = _.find(nodes, { id });
    if (found) return found;

    for(let i = 0; i < nodes.length; i++) {
        const f = findNode(nodes[i].children, id);
        if (f) return f;
    }

    return null;
}


export function getParents(node) {
    if (! node) return [];

    let ptr = node.parent;

    const result = [];

    while (ptr) {
        result.push(ptr);
        ptr = ptr.parent;
    }

    return result;
}


// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters
export function escapeRegexCharacters(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}


export function noop() {}


export function randomPick(xs) {
    if (!xs) throw new Error('Cannot pick from a null set of options');

    const choiceCount = xs.length - 1;
    const idx = Math.round(Math.random() * choiceCount);
    return xs[idx];
}


/**
 * Attempts to return the result of the given function.
 * If the function throws an exception the default value
 * will be returned
 *
 * @param fn
 * @param dflt
 * @returns {*}
 */
export function perhaps(fn, dflt) {
    try {
        return fn();
    } catch (e) {
        return dflt;
    }
}


/**
 * Takes a number and limits it to the given number
 * of digits.
 * Examples:
 *   numberFormatter(500_000, 0) :: 500k
 *   numberFormatter(5_000_000, 0) :: 5M
 *   numberFormatter(5_000_000_000, 0) :: 5B
 * @param num
 * @param digits
 * @returns {*}
 */
export function numberFormatter(num, digits = 0) {
    const si = [
        { value: 1E12, symbol: "T" },
        { value: 1E9,  symbol: "B" },
        { value: 1E6,  symbol: "M" },
        { value: 1E3,  symbol: "k" }
    ];

    for (let i = 0; i < si.length; i++) {
        if (num >= si[i].value) {
            return (num / si[i].value)
                    .toFixed(digits)
                    .replace(/\.?0+$/, "") + si[i].symbol;
        }
    }
    return num;
}


window.numberFormatter = numberFormatter;
/**
 The wix tree widget does deep comparisons.
 Having parents as refs therefore blows the callstack.
 This method will replace refs with id's.
*/
export function switchToParentIds(treeData = []) {
    _.each(treeData, td => {
        td.parent = td.parent ? td.parent.id : null;
        switchToParentIds(td.children);
    });
    return treeData;
}


/**
 *<commit_msg>Fixing build<commit_after>/*
 * Waltz - Enterprise Architecture
 * Copyright (C) 2016  Khartec Ltd.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

import _ from "lodash";
import {nest} from "d3-collection";
import {checkIsEntityRef} from "./checks";


export function notEmpty(xs) {
    return ! _.isEmpty(xs);
}


export function isEmpty(xs) {
    return _.isEmpty(xs);
}


export function mkSafe(xs = []) {
    return _.isEmpty(xs) ? [] : xs;
}


/**
 * Takes an array and groups by a key extraction function, processing values
 * with a value transformation function.
 * @param coll
 * @param keyFn
 * @param valFn
 */
export function groupAndMap(coll = [], keyFn = d => d.id, valFn = d => d) {
    return _.reduce(
        coll,
        (acc, x) => {
            const k = keyFn(x);
            const bucket = acc[k] || [];
            bucket.push(valFn(x));
            acc[k] = bucket;
            return acc;
        },
        {});
}

/**
 * Given data that looks like:
 *
 *    [ { id: "",  parentId: ?, ... } , .. ]
 *
 * Gives back an array of top level objects which have children
 * nested in them, the result looks something like:
 *
 *    [ id: "", parentId : ?, parent : {}?, children : [ .. ], ... },  .. ]
 *
 * @param nodes
 * @returns {Array}
 */
export function populateParents(nodes) {
    const byId = _.chain(_.cloneDeep(nodes))
        .map(u => _.merge(u, { children: [], parent: null }))
        .keyBy('id')
        .value();

    _.each(_.values(byId), u => {
        if (u.parentId) {
            const parent = byId[u.parentId];
            if (parent) {
                parent.children.push(u);
                u.parent = parent;
            }
        }
    });

    return _.values(byId);
}


export function buildHierarchies(nodes) {
    // only give back root element/s
    return _.reject(populateParents(nodes), n => n.parent);
}


export function findNode(nodes = [], id) {
    const found = _.find(nodes, { id });
    if (found) return found;

    for(let i = 0; i < nodes.length; i++) {
        const f = findNode(nodes[i].children, id);
        if (f) return f;
    }

    return null;
}


export function getParents(node) {
    if (! node) return [];

    let ptr = node.parent;

    const result = [];

    while (ptr) {
        result.push(ptr);
        ptr = ptr.parent;
    }

    return result;
}


// https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters
export function escapeRegexCharacters(str) {
    return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}


export function noop() {}


export function randomPick(xs) {
    if (!xs) throw new Error('Cannot pick from a null set of options');

    const choiceCount = xs.length - 1;
    const idx = Math.round(Math.random() * choiceCount);
    return xs[idx];
}


/**
 * Attempts to return the result of the given function.
 * If the function throws an exception the default value
 * will be returned
 *
 * @param fn
 * @param dflt
 * @returns {*}
 */
export function perhaps(fn, dflt) {
    try {
        return fn();
    } catch (e) {
        return dflt;
    }
}


/**
 * Takes a number and limits it to the given number
 * of digits.
 * Examples:
 *   numberFormatter(500_000, 0) :: 500k
 *   numberFormatter(5_000_000, 0) :: 5M
 *   numberFormatter(5_000_000_000, 0) :: 5B
 * @param num
 * @param digits
 * @returns {*}
 */
export function numberFormatter(num, digits = 0) {
    const si = [
        { value: 1E12, symbol: "T" },
        { value: 1E9,  symbol: "B" },
        { value: 1E6,  symbol: "M" },
        { value: 1E3,  symbol: "k" }
    ];

    for (let i = 0; i < si.length; i++) {
        if (num >= si[i].value) {
            return (num / si[i].value)
                    .toFixed(digits)
                    .replace(/\.?0+$/, "") + si[i].symbol;
        }
    }
    return num;
}


/**
 The wix tree widget does deep comparisons.
 Having parents as refs therefore blows the callstack.
 This method will replace refs with id's.
*/
export function switchToParentIds(treeData = []) {
    _.each(treeData, td => {
        td.parent = td.parent ? td.parent.id : null;
        switchToParentIds(td.children);
    });
    return treeData;
}


/**
 *
{'fraction_non_alphanumeric': 0.14043608556370776, 'fraction_numerical': 0.010540456753125968, 'mean_word_length': 2.3650904033379696, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 21, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4099996', 'n_tokens_mistral': 3164, 'n_tokens_neox': 2985, 'n_words': 953}