import type { PhpRuntimeValue } from '../_helpers/_phpTypes.ts' type CountValue = PhpRuntimeValue type CountableList = CountValue[] interface CountableAssoc { [key: string]: CountValue } export type Countable = CountableList | CountableAssoc export type CountMode = 0 | 1 | 'COUNT_NORMAL' | 'COUNT_RECURSIVE' const isCountable = (value: CountValue): value is Countable => { if (!value || typeof value !== 'object') { return false } const valuePrototype = Object.getPrototypeOf(value) return valuePrototype === Array.prototype || valuePrototype === Object.prototype } export function count(mixedVar: null | undefined, mode?: CountMode): 0 export function count(mixedVar: Countable, mode?: CountMode): number export function count(mixedVar: Countable | null | undefined, mode: CountMode = 0): number { // discuss at: https://locutus.io/php/count/ // original by: Kevin van Zonneveld (https://kvz.io) // input by: Waldo Malqui Silva (https://waldo.malqui.info) // input by: merabi // bugfixed by: Soren Hansen // bugfixed by: Olivier Louvignes (https://mg-crea.com/) // improved by: Brett Zamir (https://brett-zamir.me) // example 1: count([[0,0],[0,-4]], 'COUNT_RECURSIVE') // returns 1: 6 // example 2: count({'one' : [1,2,3,4,5]}, 'COUNT_RECURSIVE') // returns 2: 6 let cnt = 0 if (mixedVar === null || typeof mixedVar === 'undefined') { return 0 } if (typeof mixedVar !== 'object') { return 1 } const prototype = Object.getPrototypeOf(mixedVar) if (prototype !== Array.prototype && prototype !== Object.prototype) { return 1 } const recursiveMode = mode === 'COUNT_RECURSIVE' || mode === 1 if (Array.isArray(mixedVar)) { for (const key of Object.keys(mixedVar)) { cnt++ const value = mixedVar[Number(key)] if (recursiveMode && isCountable(value)) { cnt += count(value, 1) } } return cnt } for (const value of Object.values(mixedVar)) { cnt++ if (recursiveMode && isCountable(value)) { cnt += count(value, 1) } } return cnt }