Last active
August 19, 2022 07:40
-
-
Save rajibchy/074fa69e9194e76dbd750cfca693e004 to your computer and use it in GitHub Desktop.
Javascript Stack Data Source for React-Native FlatList
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // @ts-check | |
| // 10:30 AM 7/13/2021 | |
| // by rajib chy | |
| export default class DataSource { | |
| /** return copy of data array */ | |
| get data() { | |
| return this._useFilter ? [...this._shadow] : [...this._data]; | |
| } | |
| get isEndReached() { | |
| return this._fromIndex >= this._length | |
| } | |
| get refData() { | |
| return this._refData; | |
| } | |
| get length() { | |
| return this._length; | |
| } | |
| /** | |
| * | |
| * @param {number} maxStack | |
| * @param {number} initialStack | |
| * @param {boolean|void} useFilter | |
| */ | |
| constructor(maxStack, initialStack, useFilter) { | |
| this._id = randomKey; | |
| /** @type {number} */ | |
| this._maxStack = maxStack; | |
| /** @type {number} */ | |
| this._initialStack = initialStack; | |
| this._data = []; | |
| this._shadow = []; | |
| this._refData = []; | |
| this._length = 0; | |
| this._fromIndex = 0; | |
| this._toIndex = 0; | |
| this._isFiltered = false; | |
| /** @type {boolean} */ | |
| this._useFilter = useFilter === true; | |
| } | |
| /** | |
| * Update data | |
| * @param {any[]} data | |
| */ | |
| set(data) { | |
| this._reset(); | |
| this._data.length = 0; | |
| if (!Array.isArray(data)) { | |
| this._length = 0; | |
| return; | |
| } | |
| this._length = data.length; | |
| defineProperty("_data", data, this); | |
| if (this._useFilter) | |
| defineProperty("_shadow", data, this); | |
| this._updateStack(this._initialStack); | |
| } | |
| /** | |
| * | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @returns {boolean} | |
| */ | |
| exists(predicate) { | |
| const index = this._useFilter ? this._shadow.findIndex(predicate) : this._data.findIndex(predicate); | |
| return index >= 0; | |
| } | |
| resetStack() { | |
| this._fromIndex = 0; this._refData.length = 0; | |
| this._updateStack(this._initialStack); | |
| } | |
| /** | |
| * return true if stack changed | |
| * @param {number} maxStack | |
| * @returns {boolean} | |
| */ | |
| _updateStack(maxStack) { | |
| if (this._length === 0) return false; | |
| if (this._fromIndex >= this._length) { | |
| if (this._refData.length > 0) this._refData.length = 0; | |
| return false; | |
| } | |
| this._fromIndex = this._toIndex; | |
| this._toIndex = this._fromIndex + maxStack; | |
| if (this._toIndex > this._length) { | |
| this._toIndex = this._length; | |
| } | |
| this._refData = this._data.slice(this._fromIndex, this._toIndex); | |
| //console.log(this._fromIndex, this._toIndex, this._data.length) | |
| return true; | |
| } | |
| /** | |
| * | |
| * @returns {boolean} | |
| */ | |
| reUpdateStack() { | |
| if (this._length === 0) return false; | |
| if (this._useFilter) { | |
| defineProperty("_data", this._shadow, this); | |
| } | |
| if (this._toIndex > this._length) { | |
| this._toIndex = this._length; | |
| } | |
| if (this._fromIndex >= this._toIndex) { | |
| this._fromIndex = 0; this._toIndex = this._initialStack; | |
| } | |
| this._refData = this._data.slice(this._fromIndex, this._toIndex); | |
| //console.log(this._fromIndex, this._toIndex, this._data.length) | |
| return true; | |
| } | |
| updateStack() { | |
| return this._updateStack(this._maxStack); | |
| } | |
| /** | |
| * @param {any[]}data | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @returns {any} | |
| */ | |
| _getRow(data, predicate) { | |
| const index = data.findIndex(predicate); | |
| if (index < 0) return undefined; | |
| return { | |
| index: index, | |
| row: { ...data[index] } | |
| }; | |
| } | |
| /** | |
| * | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @returns {any} | |
| */ | |
| getRow(predicate) { | |
| if (this._useFilter) { | |
| return this._getRow(this._shadow, predicate); | |
| } | |
| return this._getRow(this._data, predicate); | |
| } | |
| /** | |
| * Return row copy of given index | |
| * @param {number} index | |
| * @returns {any} | |
| */ | |
| getRowByIndex(index) { | |
| if (this._useFilter) { | |
| if (index < 0 || index >= this._shadow.length) return undefined; | |
| return { ...this._shadow[index] }; | |
| } | |
| if (index < 0 || index >= this._length) return undefined; | |
| return { ...this._data[index] }; | |
| } | |
| /** | |
| * @param {any[]}data | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @param {(row:any, index:number)=>number}updater | |
| * @returns {{index:number;row?:any;}} | |
| */ | |
| _updateRow(data, predicate, updater) { | |
| let index = data.findIndex(predicate); | |
| if (index < 0) return { index }; | |
| const nRow = { ...data[index] }; | |
| index = updater(nRow, index); | |
| if (index < 0) return { index }; | |
| this._data[index] = nRow; | |
| return { row: nRow, index: index }; | |
| } | |
| /** | |
| * | |
| * @param {any}row | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @param {boolean|void} noUpdate | |
| * @returns {number} | |
| */ | |
| replaceRow(row, predicate, noUpdate) { | |
| // console.log('replaceRow'); | |
| if (typeof (noUpdate) === "undefined") noUpdate = false; | |
| if (this._useFilter) { | |
| const index = this._shadow.findIndex(predicate); | |
| if (index < 0) return index; | |
| this._shadow[index] = row; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return index; | |
| } | |
| const index = this._data.findIndex(predicate); | |
| if (index < 0) return index; | |
| this._data[index] = row; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return index; | |
| } | |
| /** | |
| * | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @param {(row:any, index:number)=>number}updater | |
| * @param {boolean|void} noUpdate | |
| * @returns {number} | |
| */ | |
| updateRow(predicate, updater, noUpdate) { | |
| // console.log('updateRow'); | |
| if (typeof (noUpdate) === "undefined") noUpdate = false; | |
| if (this._useFilter) { | |
| const { row, index } = this._updateRow(this._shadow, predicate, updater); | |
| if (index < 0) return index; | |
| this._shadow[index] = row; | |
| // const findex = this._data.findIndex(predicate); | |
| // if (findex < 0) return index; | |
| // this._data[findex] = row; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return index; | |
| } | |
| const { row, index } = this._updateRow(this._data, predicate, updater); | |
| if (index < 0) return index; | |
| this._data[index] = row; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return index; | |
| } | |
| /** | |
| * | |
| * @param {(value: any, index: number, obj: any[]) => unknown} predicate | |
| * @param {boolean|void} noUpdate | |
| * @returns {number} | |
| */ | |
| deleteRow(predicate, noUpdate) { | |
| // console.log('deleteRow'); | |
| if (typeof (noUpdate) === "undefined") noUpdate = false; | |
| if (this._useFilter) { | |
| const sIndex = this._shadow.findIndex(predicate); | |
| if (sIndex < 0) return sIndex; | |
| this._shadow.splice(sIndex, 1); | |
| this._length = this._shadow.length; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return sIndex; | |
| } | |
| const index = this._data.findIndex(predicate); | |
| if (index < 0) return index; | |
| this._data.splice(index, 1); | |
| this._length = this._data.length; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return index; | |
| } | |
| /** | |
| * | |
| * @param {any} row | |
| * @param {boolean|void} noUpdate | |
| * @returns {number} | |
| */ | |
| unshift(row, noUpdate) { | |
| // console.log('unshift'); | |
| if (typeof (noUpdate) === "undefined") noUpdate = false; | |
| if (this._useFilter) { | |
| this._shadow.unshift(row); | |
| this._length = this._shadow.length; | |
| if (!noUpdate) this.reUpdateStack(); | |
| return this._length; | |
| } | |
| this._data.unshift(row); | |
| this._length = this._data.length; | |
| return this._length; | |
| } | |
| /** | |
| * | |
| * @param {string} propName | |
| * @param {(a:any,b:any)=>number} comparator | |
| * @param {boolean|void} skipFirstRow | |
| */ | |
| onSortOrder(propName, comparator, skipFirstRow) { | |
| this._reset(); | |
| if (typeof (skipFirstRow) === "undefined") skipFirstRow = false; | |
| if (this._useFilter) { | |
| if (skipFirstRow) { | |
| let index = 0; | |
| defineProperty("_shadow", this._shadow.sort((aRow, bRow) => { | |
| if (index === 0) return index++, 0; | |
| return comparator(aRow[propName], bRow[propName]); | |
| }), this); | |
| } else { | |
| defineProperty("_shadow", this._shadow.sort((aRow, bRow) => comparator(aRow[propName], bRow[propName])), this); | |
| } | |
| defineProperty("_data", this._shadow, this); | |
| } else { | |
| if (skipFirstRow) { | |
| let index = 0; | |
| defineProperty("_data", this._data.sort((aRow, bRow) => { | |
| if (index === 0) return index++, 0; | |
| return comparator(aRow[propName], bRow[propName]); | |
| }), this); | |
| } else { | |
| defineProperty("_data", this.data.sort((aRow, bRow) => comparator(aRow[propName], bRow[propName])), this); | |
| } | |
| } | |
| this._updateStack(this._initialStack); | |
| } | |
| _reset() { | |
| this._fromIndex = 0; this._toIndex = 0; | |
| this._refData.length = 0; this._isFiltered = false; | |
| } | |
| clearFilter() { | |
| if (!this._useFilter) throw new Error("filter doesn't supported while you don't enable it."); | |
| this._reset(); | |
| defineProperty("_data", this._shadow, this); | |
| this._length = this._data.length; | |
| return this._updateStack(this._initialStack); | |
| } | |
| /** | |
| * | |
| * @param {(value: any, index: number, array: any[]) => unknown} predicate | |
| * @returns {boolean} | |
| */ | |
| filter(predicate) { | |
| if (!this._useFilter) throw new Error("filter doesn't supported while you don't enable it."); | |
| this._reset(); | |
| defineProperty("_data", this._shadow.filter(predicate), this); | |
| this._length = this._data.length; | |
| return this._updateStack(this._initialStack); | |
| } | |
| /** | |
| * | |
| * @param {string} query | |
| * @param {string} prop | |
| * @param {boolean|void} exact | |
| */ | |
| select(query, prop, exact) { | |
| if (!this._useFilter) throw new Error("Select doesn't supported while you don't enable it."); | |
| this._reset(); | |
| defineProperty("_data", ( | |
| exact ? this._shadow.filter(row => row[prop] === query) : | |
| this._shadow.filter(row => row[prop].indexOf(query) > -1) | |
| ), this); | |
| this._length = this._data.length; | |
| return this._updateStack(this._initialStack); | |
| } | |
| /** | |
| * Sum all row(s) without given props | |
| * @param {string[]} propList | |
| * @param {((result:any, row:any, index?:number)=>void)|void} fill | |
| * @param {boolean|void} isExcept | |
| * @returns {any} | |
| */ | |
| sum(propList, fill, isExcept) { | |
| // by rajib chy | |
| const _fill = fill || (() => void 0); | |
| const _isExcept = typeof (isExcept) === "undefined" ? true : isExcept; | |
| if (this._useFilter) { | |
| if (this._shadow.length === 0) return {}; | |
| const props = _isExcept ? Object.keys(this._shadow[0]).filter(a => !propList.includes(a)) : Object.keys(this._shadow[0]).filter(a => propList.includes(a)); | |
| return this._shadow.reduce((result, row, index) => { | |
| if (!result) result = {}; | |
| _fill(row, result, index); | |
| props.forEach(prop => { | |
| if (!result[prop]) { | |
| result[prop] = row[prop] | |
| return; | |
| } | |
| result[prop] += row[prop]; | |
| }); | |
| return result; | |
| }, {}); | |
| } | |
| if (this._data.length === 0) return {}; | |
| const props = _isExcept ? Object.keys(this._data[0]).filter(a => !propList.includes(a)) : Object.keys(this._data[0]).filter(a => propList.includes(a)); | |
| return this._data.reduce((result, row, index) => { | |
| if (!result) result = {}; | |
| _fill(row, result, index); | |
| props.forEach(prop => { | |
| if (propList.includes(prop)) return; | |
| if (!result[prop]) { | |
| result[prop] = row[prop] | |
| return; | |
| } | |
| result[prop] += row[prop]; | |
| }); | |
| return result; | |
| }, {}); | |
| } | |
| destroy() { | |
| if (this._length === 0) return; | |
| this._reset(); | |
| this._data.length = 0; | |
| if (this._useFilter) | |
| this._shadow.length = 0; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment