Skip to content

Instantly share code, notes, and snippets.

@rajibchy
Last active August 19, 2022 07:40
Show Gist options
  • Select an option

  • Save rajibchy/074fa69e9194e76dbd750cfca693e004 to your computer and use it in GitHub Desktop.

Select an option

Save rajibchy/074fa69e9194e76dbd750cfca693e004 to your computer and use it in GitHub Desktop.
Javascript Stack Data Source for React-Native FlatList
// @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