UNPKG

@bitbybit-dev/base

Version:

Bit By Bit Developers Base CAD Library to Program Geometry

682 lines (681 loc) 20.3 kB
import * as Inputs from "../inputs"; /** * Contains various list methods. * <div> * <img src="../assets/images/blockly-images/math/math.svg" alt="Blockly Image"/> * </div> */ export class Lists { /** * Gets an item from the list by using a 0 based index * @param inputs a list and an index * @returns item * @group get * @shortname item by index * @drawable false */ getItem(inputs) { if (inputs.index < 0 || inputs.index >= inputs.list.length) { throw new Error("Index out of bounds"); } let result; if (inputs.clone) { result = structuredClone(inputs.list[inputs.index]); } else { result = inputs.list[inputs.index]; } return result; } /** * Gets items randomly by using a threshold * @param inputs a list and a threshold for randomization of items to remove * @returns list with remaining items * @group get * @shortname random get threshold * @drawable false */ randomGetThreshold(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } const newList = []; for (let i = 0; i < inputs.list.length; i++) { if (Math.random() < inputs.threshold) { newList.push(res[i]); } } return newList; } /** * Gets a sub list between start and end indexes * @param inputs a list and start and end indexes * @returns sub list * @group get * @shortname sublist * @drawable false */ getSubList(inputs) { let result; if (inputs.clone) { result = structuredClone(inputs.list.slice(inputs.indexStart, inputs.indexEnd)); } else { result = inputs.list.slice(inputs.indexStart, inputs.indexEnd); } return result; } /** * Gets nth item in the list * @param inputs a list and index * @returns list with filtered items * @group get * @shortname every n-th * @drawable false */ getNthItem(inputs) { let cloned = inputs.list; if (inputs.clone) { cloned = structuredClone(inputs.list); } const result = []; for (let i = 0; i < cloned.length; i++) { if ((i + inputs.offset) % inputs.nth === 0) { result.push(cloned[i]); } } return result; } /** * Gets elements by pattern * @param inputs a list and index * @returns list with filtered items * @group get * @shortname by pattern * @drawable false */ getByPattern(inputs) { const { list, pattern } = inputs; if (!pattern || pattern.length === 0) { throw new Error("Pattern is empty or does not exist"); } const patternLength = pattern.length; const listLength = list.length; const result = []; if (patternLength >= listLength) { list.forEach((item, index) => { if (pattern[index] === true) { result.push(item); } }); } else { const repeatedPattern = []; const repeatPatternTimes = Math.ceil(listLength / patternLength); for (let i = 0; i < repeatPatternTimes; i++) { repeatedPattern.push(...pattern); } list.forEach((item, index) => { if (repeatedPattern[index] === true) { result.push(item); } }); } return result; } /** * Merge elements of lists on a given level and flatten output if needed * @param inputs lists, level and flatten data * @returns list with merged lists and flattened lists * @group get * @shortname merge levels * @drawable false */ mergeElementsOfLists(inputs) { const lists = inputs.lists; const level = inputs.level; const elToMerge = []; const result = []; lists.forEach(list => { // flatten to certain level; const elementsToMerge = list.flat(level); elToMerge.push(elementsToMerge); }); const lengthMerge = this.getLongestListLength({ lists: elToMerge }); for (let i = 0; i < lengthMerge; i++) { const temp = []; for (let j = 0; j < elToMerge.length; j++) { const element = elToMerge[j][i]; if (element !== undefined) { temp.push(element); } } if (temp.length > 0) { result.push(temp); } } let final = []; if (level > 0) { for (let i = 0; i < level; i++) { if (i === level - 1 && i !== 0) { final[i - 1].push(result); } else if (i === level - 1) { final.push(result); } else { final.push([]); } } } else { final = result; } return final; } /** * Gets the longest list length from the list of lists * @param inputs a list of lists * @returns number of max length * @group get * @shortname longest list length * @drawable false */ getLongestListLength(inputs) { let longestSoFar = 0; if (inputs.lists) { inputs.lists.forEach(l => { if (l.length > longestSoFar) { longestSoFar = l.length; } }); return longestSoFar; } else { return undefined; } } /** * Reverse the list * @param inputs a list and an index * @returns item * @group edit * @shortname reverse * @drawable false */ reverse(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } return res.reverse(); } /** * Flip 2d lists - every nth element of each list will form a separate list * @param inputs a list of lists to flip * @returns item * @group edit * @shortname flip lists * @drawable false */ flipLists(inputs) { if (inputs.list.length > 0) { const lengthOfFirstList = inputs.list[0].length; let allListsSameLength = true; inputs.list.forEach(l => { if (l.length !== lengthOfFirstList) { allListsSameLength = false; } }); if (allListsSameLength) { const result = []; for (let i = 0; i < lengthOfFirstList; i++) { const newList = []; inputs.list.forEach(l => { newList.push(l[i]); }); result.push(newList); } return result; } else { throw new Error("Lists are not of the same length"); } } else { throw new Error("List is empty"); } } /** * Group in lists of n elements * @param inputs a list * @returns items grouped in lists of n elements * @group edit * @shortname group elements * @drawable false */ groupNth(inputs) { const groupElements = (inputs) => { const { nrElements, list, keepRemainder } = inputs; const nrElementsInGroup = nrElements; const result = []; let currentGroup = []; list.forEach((item, index) => { currentGroup.push(item); if ((index + 1) % nrElementsInGroup === 0) { result.push(currentGroup); currentGroup = []; } if (currentGroup.length > 0 && keepRemainder && index === list.length - 1) { result.push(currentGroup); } }); return result; }; return groupElements(inputs); } /** * Get the depth of the list * @param inputs a list * @returns number of depth * @group get * @shortname max list depth * @drawable false */ getListDepth(inputs) { let levels = 0; let deeperLevelsExist = true; let flatRes = inputs.list; while (deeperLevelsExist) { let foundArray = false; for (let i = 0; i < flatRes.length; i++) { if (Array.isArray(flatRes[i])) { foundArray = true; } } flatRes = flatRes.flat(); if (foundArray) { levels++; } else { levels++; deeperLevelsExist = false; } } return levels; } /** * Gets the length of the list * @param inputs a length list * @returns a number * @group get * @shortname list length * @drawable false */ listLength(inputs) { return inputs.list.length; } /** * Add item to the list * @param inputs a list, item and an index * @returns list with added item * @group add * @shortname add item * @drawable false */ addItemAtIndex(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.index >= 0 && inputs.index <= res.length) { res.splice(inputs.index, 0, inputs.item); } return res; } /** * Adds item to the list of provided indexes * @param inputs a list, item and an indexes * @returns list with added item * @group add * @shortname add item at indexes * @drawable false */ addItemAtIndexes(inputs) { let cloned = inputs.list; if (inputs.clone) { cloned = structuredClone(inputs.list); } let cloneIndexes = [...inputs.indexes]; cloneIndexes = cloneIndexes.filter(index => index >= 0 && index <= cloned.length); cloneIndexes.sort((a, b) => a - b); cloneIndexes.forEach((index, i) => { if (index >= 0 && index + i <= cloned.length) { cloned.splice(index + i, 0, inputs.item); } }); return cloned; } /** * Adds items to the list of provided indexes matching 1:1, first item will go to first index provided, etc. * @param inputs a list, items and an indexes * @returns list with added items * @group add * @shortname add items * @drawable false */ addItemsAtIndexes(inputs) { if (inputs.items.length !== inputs.indexes.length) { throw new Error("Items and indexes must have the same length"); } for (let i = 0; i < inputs.indexes.length; i++) { if (i > 0) { const prev = inputs.indexes[i - 1]; if (prev > inputs.indexes[i]) { throw new Error("Indexes must be in ascending order"); } } } let cloned = inputs.list; if (inputs.clone) { cloned = structuredClone(inputs.list); } const cloneIndexes = [...inputs.indexes]; cloneIndexes.forEach((index, i) => { if (index >= 0 && index + i <= cloned.length) { cloned.splice(index + i, 0, inputs.items[i]); } }); return cloned; } /** * Remove item from the list * @param inputs a list and index * @returns list with removed item * @group remove * @shortname remove item * @drawable false */ removeItemAtIndex(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.index >= 0 && inputs.index <= res.length) { res.splice(inputs.index, 1); } return res; } /** * Remove items from the list of provided indexes * @param inputs a list and indexes * @returns list with removed items * @group remove * @shortname remove items * @drawable false */ removeItemsAtIndexes(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } const cloneIndexes = [...inputs.indexes]; cloneIndexes.sort((a, b) => b - a); cloneIndexes.forEach(index => { if (index >= 0 && index < res.length) { res.splice(index, 1); } }); return res; } /** * Remove all items from the list * @param inputs a list * @returns The length is set to 0 and same array memory object is returned * @group remove * @shortname remove all items * @drawable false */ removeAllItems(inputs) { inputs.list.length = 0; return inputs.list; } /** * Remove item from the list * @param inputs a list and index * @returns list with removed item * @group remove * @shortname every n-th * @drawable false */ removeNthItem(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } const result = []; for (let i = 0; i < res.length; i++) { if ((i + inputs.offset) % inputs.nth !== 0) { result.push(res[i]); } } return result; } /** * Removes items randomly by using a threshold * @param inputs a list and a threshold for randomization of items to remove * @returns list with removed items * @group remove * @shortname random remove threshold * @drawable false */ randomRemoveThreshold(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } const newList = []; for (let i = 0; i < inputs.list.length; i++) { if (Math.random() > inputs.threshold) { newList.push(res[i]); } } return newList; } /** * remove duplicate numbers from the list * @param inputs a list of numbers * @returns list with unique numbers * @group remove * @shortname remove duplicates * @drawable false */ removeDuplicateNumbers(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } return res.filter((value, index, self) => self.indexOf(value) === index); } /** * remove duplicate numbers from the list with tolerance * @param inputs a list of numbers and the tolerance * @returns list with unique numbers * @group remove * @shortname remove duplicates tol * @drawable false */ removeDuplicateNumbersTolerance(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } return res.filter((value, index, self) => self.findIndex(s => Math.abs(s - value) < inputs.tolerance) === index); } /** * Add item to the end of the list * @param inputs a list and an item * @returns list with added item * @group add * @shortname add item to list * @drawable false */ addItem(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } res.push(inputs.item); return res; } /** * Add item to the beginning of the list * @param inputs a list and an item * @returns list with added item * @group add * @shortname prepend item to list * @drawable false */ prependItem(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } res.unshift(inputs.item); return res; } /** * Add item to the beginning or the end of the list * @param inputs a list, item and an option for first or last position * @returns list with added item * @group add * @shortname item at first or last * @drawable false */ addItemFirstLast(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.position === Inputs.Lists.firstLastEnum.first) { res.unshift(inputs.item); } else { res.push(inputs.item); } return res; } /** * Creates an empty list * @returns an empty array list * @group create * @shortname empty list * @drawable false */ createEmptyList() { return []; } /** * Repeat the item and add it in the new list * @param inputs an item to multiply * @returns list * @group create * @shortname repeat * @drawable false */ repeat(inputs) { const result = []; for (let i = 0; i < inputs.times; i++) { result.push(inputs.item); } return result; } /** * Repeat the list items by adding them in the new list till the certain length of the list is reached * @param inputs a list to multiply and a length limit * @returns list * @group create * @shortname repeat in pattern * @drawable false */ repeatInPattern(inputs) { // will repeat the items provided in the patten till the certain length of the list is reached let inpList = inputs.list; if (inputs.clone) { inpList = structuredClone(inputs.list); } const res = []; let counter = 0; let index = 0; while (counter < inputs.lengthLimit) { res.push(inpList[index]); index++; if (index === inpList.length) { index = 0; } counter++; } return res; } /** * Sort the list of numbers in ascending or descending order * @param inputs a list of numbers to sort and an option for ascending or descending order * @returns list * @group sorting * @shortname sort numbers * @drawable false */ sortNumber(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.orderAsc) { return res.sort((a, b) => a - b); } else { return res.sort((a, b) => b - a); } } /** * Sort the list of texts in ascending or descending order alphabetically * @param inputs a list of texts to sort and an option for ascending or descending order * @returns list * @group sorting * @shortname sort texts * @drawable false */ sortTexts(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.orderAsc) { return res.sort(); } else { return res.sort().reverse(); } } /** * Sort by numeric JSON property value * @param inputs a list to sort, a property to sort by and an option for ascending or descending order * @returns list * @group sorting * @shortname sort json objects * @drawable false */ sortByPropValue(inputs) { let res = inputs.list; if (inputs.clone) { res = structuredClone(inputs.list); } if (inputs.orderAsc) { return res.sort((a, b) => a[inputs.property] - b[inputs.property]); } else { return res.sort((a, b) => b[inputs.property] - a[inputs.property]); } } }