How optimise nested looping in JavaScript? - javascript

I am new to JavaScript ES6. I want to convert nested looping to single looping in my code. I have some examples below can anyone help me out how to make it single loop. Is it possible for my below code. As I know that in JavaScript Nested loop is not good for performance.
No 1:
getCountryNames = (cList, countryCodeList) => {
const list = [];
countryCodeList.forEach(code => {
cList.forEach(cjson => {
if (cjson.code === code) {
list.push(cjson.label);
}
});
});
return list;
}
No 2:
getITestList = () => {
const msgArr = this.state.bufferMsgList;
const arr = [];
msgArr.forEach(msg => {
let flag = true;
if (arr.length > 0) {
arr.forEach(data => {
if (data.id === msg.group_entity_id) {
flag = false;
}
});
}
if (flag) {
const json = {
id: msg.group_entity_id,
code: msg.group_entity_code,
};
arr.push(json);
}
});
return arr;
}
No 3:
getMsgWithTest = (list) => {
const responseArr = [];
if (list.length > 0) {
list.forEach(payload => {
const messageList = payload.messages;
if (messageList) {
messageList.forEach(msg => {
const mjson = Object.assign({}, msg);
mjson.group_entity_code = payload.group_entity_code;
mjson.group_entity_id = payload.group_entity_id;
responseArr.push(mjson);
});
}
});
}
return responseArr;
}
No 4:
this.state.defaultMessageList.forEach(payload => {
if (payload.messages) {
payload.messages.forEach(msg => {
filteredRegionBasedOnMsg.push(msg.regions);
filteredCountryBasedOnMsg.push(msg.country_codes);
});
}
});
No 5:
this.state.defaultMessageList.forEach(payload => {
if (payload.messages) {
payload.messages.forEach(msg => {
if (msg.status === 'ACTIVE') {
filteredCountryBasedOnMsg.push(msg.country_codes);
}
});
}
});

Related

Run Script as new content loaded , how can i listen to DOM change?

Hi im working on a Chrome extension that adds badges to users, now the problem is i have to refresh the page everytime so the badges can load because there are client-side changes . How can i watch events change so i run the function on first time page load ?
I read something about input event listener or MutationObserver but im not sure how can i implement that , Any help would be much appreciated .
CODE:
function Foreground() {
let users = null;
let queries = [];
let userIds = [];
document.addEventListener("DOMContentLoaded", function(event) {});
window.addEventListener('load', () => {
fetchUsersAndQueries();
chrome.runtime.onMessage.addListener(async(message, sender, res) => {
if (message.users) {
fetchUsersAndQueries();
if (users) {
return res(users);
} else {
return res([]);
}
}
if (message.refresh) {
try {
assignBadges();
} catch (error) {
console.log(error.message);
}
}
return true;
});
let done = false;
setInterval(() => {
if (done) {
return;
}
if (users) {
done = true;
try {
assignBadges();
} catch (error) {
console.log(error.message);
}
}
}, 500);
});
async function fetchUsersAndQueries() {
userIds = await getAPIUserIds();
let isStop = false;
setInterval(() => {
if (isStop) {
return;
}
const parasiteContainer = document.querySelector('#parasite-container');
if (parasiteContainer) {
if (parasiteContainer.shadowRoot) {
try {
const roster1 = parasiteContainer.shadowRoot.querySelector(`[name="roster1"]`);
const roster2 = parasiteContainer.shadowRoot.querySelector(`[name="roster2"]`);
if (!roster1) {
return
};
if ([...roster1.children].length === 1) {
if (roster1.firstElementChild.children.length === 1) {
if (roster1.firstElementChild.firstElementChild.length === 1) {
const fTeam = [...roster1.firstElementChild.firstElementChild.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.firstElementChild.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.firstElementChild.firstElementChild.children]);
queries.push([...roster2.firstElementChild.firstElementChild.firstElementChild.children]);
isStop = true;
} else {
const fTeam = [...roster1.firstElementChild.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.firstElementChild.children]);
queries.push([...roster2.firstElementChild.firstElementChild.children]);
isStop = true;
}
} else {
const fTeam = [...roster1.firstElementChild.children].map(item => getUsername(item));
const sTeam = [...roster2.firstElementChild.children].map(item => getUsername(item));
users = fTeam.concat(sTeam).flat();
queries.push([...roster1.firstElementChild.children]);
queries.push([...roster2.firstElementChild.children]);
isStop = true;
}
}
queries.forEach(query => {
query.map(item => {
if (item.children.length > 1) {
[...item.children].map(child => {
// const container = child.querySelector('.sc-hCQDas'); // Classname might change in the future.
const container = child.firstElementChild ? .firstElementChild ? .firstElementChild ? .firstElementChild ? .children[2];
if (container) {
container.insertAdjacentHTML(
'beforeend',
createBadge(badgesResponse.data.exists, child.dataset.userId)
);
}
});
} else {
// const container = item.querySelector('.sc-hCQDas'); // Classname might change in the future.
const container = item.firstElementChild ? .firstElementChild ? .firstElementChild ? .children[0];
if (container) {
container.insertAdjacentHTML(
'beforeend',
createBadge(badgesResponse.data.exists, item.dataset.userId)
);
}
}
});
});
}

The length of array is showing zero

I am trying to return the correct count of messages (length of listMessage) from the function below. I can retrieve correct message Objects into listMessage, but its length is always zero.
checkLastMsg = () =>
{
var groupChatId = null;
var listMessage = [];
// console.log(this.peerUserId);
if (this.hashString(this.currentUserId) <= this.peerUserId)
{
groupChatId = `${this.currentUserId}-${this.peerUserId}`;
}
else
{
groupChatId = `${this.peerUserId}-${this.currentUserId}`;
}
// console.log(groupChatId);
myFirestore
.collection(AppString.NODE_MESSAGES)
.doc(groupChatId)
.collection(groupChatId)
.onSnapshot(
snapshot =>
{
snapshot.docChanges().forEach(change =>
{
if (change.type === AppString.DOC_ADDED)
{
listMessage.push(change.doc.data())
}
})
},
err =>
{
this.props.showToast(0, err.toString())
})
// console.log(listMessage.length);
console.log(listMessage.length);
}
Could anyone check if something is wrong here and how I can fix this issue?
Like everybody said, the code is asynchronous, hence you can make work like this:
checkLastMsg = async () =>
{
var groupChatId = null;
var listMessage = [];
// console.log(this.peerUserId);
if (this.hashString(this.currentUserId) <= this.peerUserId)
{
groupChatId = `${this.currentUserId}-${this.peerUserId}`;
}
else
{
groupChatId = `${this.peerUserId}-${this.currentUserId}`;
}
// console.log(groupChatId);
await myFirestore
.collection(AppString.NODE_MESSAGES)
.doc(groupChatId)
.collection(groupChatId)
.onSnapshot(
snapshot =>
{
snapshot.docChanges().forEach(change => {
if (change.type === AppString.DOC_ADDED) {
listMessage.push(change.doc.data())
}
})
},
err => {
this.props.showToast(0, err.toString())
}
)
// console.log(listMessage.length);
console.log(listMessage.length);
}```
The Firebase code running asynchronously. Try to maybe log the list in the callback function or something like that

javascript recursive function memory leak

I am not good at English. Successfully make recursive call function. However, there is a memory leak for some reason. The question is that there is no return. The purpose of this feature is to view and explore objects, arrays, and the rest of their properties.
How do I change the code if I have a problem with my return?
Thank you in advance.
I was able to know the cause of the memory leak through Google dev tools profiles.
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
recursionProperty(null, {foo:'bar', baz: ['x','y']}, (prop, obj) => console.log(prop, obj));
my original code
import _ from 'lodash';
import fs from 'fs';
import path from 'path';
import errors from '#feathersjs/errors';
import connections from '../../../connections';
import config from '../../../config';
/**
* #param req
* #param serviceItem
* #param query
* #returns {Promise<any>}
*/
const getServicePromise = async (req, serviceItem, query) => {
let serviceName = serviceItem;
if (typeof serviceItem !== 'string') {
serviceName = `datasets/${serviceItem.name}`;
}
return new Promise(async (resolve, reject) => {
let result;
let objResult;
try {
result = await req.app.service(serviceName).find(query);
} catch (e) {
result = null;
console.log(e);
}
// console.log(result);
if (result) {
if (typeof serviceItem !== 'string') {
objResult = { [serviceItem.name]: result.data };
} else {
objResult = { [serviceName]: result.data };
}
resolve(objResult);
} if (result === null) {
objResult = { [serviceName]: [] };
resolve(objResult);
} else {
reject({
error: 'Not found data.'
});
}
});
};
/**
* 파일 경로 프로퍼티를 찾는 재귀함수
* 객체, 배열, 원시타입 등 여러 타입이 섞여있어도 사용 가능
* #param prop
* #param obj
* #param fn
*/
function recursionProperty(prop, obj, fn) {
if (Array.isArray(obj)) {
obj.forEach(item => recursionProperty('files', item, fn));
} else if (obj instanceof Object) {
Object.keys(obj).forEach(prop => {
const value = obj[prop];
recursionProperty(prop, value, fn);
});
} else {
fn(prop, obj);
}
}
/**
* #param req
* #returns {Promise<{any}>}
*/
const getService = async req => {
const result = {};
const serverPath = [];
const { sheet, dataset, icon } = req.data;
const iconState = Object.prototype.hasOwnProperty.call(req.data, 'icon');
const sheetState = Object.prototype.hasOwnProperty.call(req.data, 'sheet');
const datasetState = Object.prototype.hasOwnProperty.call(req.data, 'dataset');
try {
// sheets
if (sheetState) {
const itemList = ['sheets'];
if (sheet.length === 0) {
const query = {
query: {
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else if (sheet.length > 0) {
const query = {
query: {
_id: {
$in: sheet,
},
},
};
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
} else {
result.sheets = [];
}
} else {
result.sheets = [];
}
// 파일 경로 구하기
if (sheet) {
const { sheets } = result;
// const filePath = [];
recursionProperty('files', sheets, (prop, value) => {
// 여기서 원하는 필드인지 검색후 처리함
if (prop === 'fullPath' && fs.existsSync(path.join(__dirname, '../../../../files', value))) {
// filePath.push(path.join(__dirname, '../../../../files', value));
serverPath.push(value);
}
});
// const deduplication = Array.from(new Set(serverPath));
// const deduplicationPath = await deduplicationFilePath(deduplication);
//
// Object.assign(result, { filePath: deduplicationPath });
} else {
// result.filePath = [];
}
// files
if (sheet) {
const deduplicationFiles = Array.from(new Set(serverPath));
if (deduplicationFiles.length > 0) {
const query = {
query: {
$sort: {
createdAt: -1,
},
fullPath: {
$in: deduplicationFiles,
},
}
};
const files = await req.app.service('files').find(query);
Object.assign(result, { files: files.data });
} else {
result.files = [];
}
} else {
result.files = [];
}
// dataset
if (datasetState) {
const query = {
query: {
// $limit: 100000
}
};
if (dataset.length === 0) {
const meta = await req.app.service('datasets/_meta_').find();
Object.assign(result, { _meta_: meta });
const db = await connections.getConnection(connections.DATASETS_DB);
const collectionNames = _.filter(await db.client.db(config.database_datasets.dbname).listCollections().toArray(), o => o.name !== '_meta_');
// collectionNames.forEach(str => {
// const detectA = iconvDetect.detect(Buffer.from(str.name));
// console.log('collection type', str.name, detectA);
// });
await Promise.all(meta.map(serviceItem => {
// const detectA = iconvDetect.detect(Buffer.from(serviceItem.key));
// console.log('meta type', serviceItem.key, detectA);
return getServicePromise(req, `datasets/${serviceItem.key}`, query);
})).then(data => {
Object.assign(result, { datasets: data });
});
} else if (dataset.length > 0) {
const metaQuery = {
query: {
$sort: {
createdAt: -1,
},
key: {
$in: dataset
}
}
};
const meta = await req.app.service('datasets/_meta_').find(metaQuery);
// console.log(meta);
Object.assign(result, { _meta_: meta });
await Promise.all(dataset.map(serviceItem => getServicePromise(req, `datasets/${serviceItem}`, query))).then(data => {
const d = Array.from(new Set(data));
const s = d.filter(item => item !== null);
if (d.length > 0) {
Object.assign(result, { datasets: s });
} else {
result.datasets = [];
result._meta_ = [];
}
});
} else {
result.datasets = [];
result._meta_ = [];
}
} else {
result.datasets = [];
result._meta_ = [];
}
if (iconState) {
const itemList = ['iconCategories', 'iconItems'];
const query = {};
if (icon.length === 0) {
await Promise.all(itemList.map(serviceItem => getServicePromise(req, serviceItem, query))).then(data => {
data.forEach(item => {
Object.assign(result, item);
});
});
}
} else {
result.iconCategories = [];
result.iconItems = [];
}
} catch (e) {
throw new errors.BadRequest('The data is invalid.', e);
}
return result;
};
export default getService;
There is most likely no memory leak in your code. Yes, recursive functions can be more memory aggressive than normal functions, because the call stack can grow very quickly, but remember that all functions will implicitly return even if there is no return statement. (Imagine that there is always a return undefined; line at the end of all your functions)
When doing a recursion, call stack will grow until you reach the bottom of a recursion branch (no function returns until then). Once the recursion branch end is reached, in your case this happens anytime you reach your else block, call stack will 'collapse' and all functions preceding will 'return'.
Memory will ultimately be freed by garbage collection as required.

javascript array filter very slow in REACT

I have a custom listbox, a div that contains a vertical list of other div children. And I have input for search something else in the list. It's working but in large data, it's working very slowly.
Also search criterion produce dynamically with column chooser. How can i increase search performance.
Firsly, prepare filter data for search and keeping state on the page load
prepareFilterData(allData) {
const filteredData = [];
let columnChooser = JSON.parse(getItemFromLocalStorage("ColumnData"));
allData.map(item => {
var data = "";
columnChooser.map(element => {
var newData = { value: item[element.value], format: element.format };
var filterItem = getFilterDataFormat(newData);
data += filterItem + " ";
});
filteredData.push(data);
});
this.setState({
filteredData: filteredData
});
}
Secondly, When user enter an char to textbox, i'm checking filteredData
filterList() {
const updatedList = this.state.allData.length > 0 ? this.state.allData : [];
var filteredData = [];
filteredData = updatedList.filter((item, index) => {
const data = this.state.filteredData[index];
return data.indexOf(this.state.searchInputValue) !== -1;
});
return filteredData;
}
This is input statement
<input
id="searchBox"
type="text"
className="filter-input empty"
placeholder="Search"
onChange={this.filterList}
value={this.props.state.searchInputValue}
style={{ width: "100%" }} />
Using a standard for loop can significantly increase the performance, especially in your case where you're using indexOf which is causing another iteration in your filter. The filter operation uses callbacks and it's often used because of the simpler syntax but it's these callback that make the operation to be slower especially on big data.
Read more here.
I found the solution.
SOLUTION:
I create a util.js in my project, and I called createFilter function.
import Fuse from "fuse.js";
import { toTrLowerCase } from "./process";
function flatten(array) {
return array.reduce((flat, toFlatten) => flat.concat(Array.isArray(toFlatten) ? flatten(toFlatten) : toFlatten), []);
}
export function getValuesForKey(key, item) {
const keys = key.split(".");
let results = [item];
keys.forEach(_key => {
const tmp = [];
results.forEach(result => {
if (result) {
if (result instanceof Array) {
const index = parseInt(_key, 10);
if (!isNaN(index)) {
return tmp.push(result[index]);
}
result.forEach(res => {
tmp.push(res[_key]);
});
} else if (result && typeof result.get === "function") {
tmp.push(result.get(_key));
} else {
tmp.push(result[_key]);
}
}
});
results = tmp;
});
// Support arrays and Immutable lists.
results = results.map(r => (r && r.push && r.toArray ? r.toArray() : r));
results = flatten(results);
return results.filter(r => typeof r === "string" || typeof r === "number");
}
export function searchStrings(strings, term, { caseSensitive, fuzzy, sortResults, exactMatch } = {}) {
strings = strings.map(e => e.toString());
try {
if (fuzzy) {
if (typeof strings.toJS === "function") {
strings = strings.toJS();
}
const fuse = new Fuse(
strings.map(s => {
return { id: s };
}),
{ keys: ["id"], id: "id", caseSensitive, shouldSort: sortResults }
);
return fuse.search(term).length;
}
return strings.some(value => {
try {
if (!caseSensitive) {
value = value.toLowerCase();
}
if (exactMatch) {
term = new RegExp("^" + term + "$", "i");
}
if (value && value.search(term) !== -1) {
return true;
}
return false;
} catch (e) {
return false;
}
});
} catch (e) {
return false;
}
}
export function createFilter(term, keys, options = { caseSensitive: false, fuzzy: false, sortResults: false, exactMatch: false }) {
debugger;
return item => {
if (term === "") {
return true;
}
if (!options.caseSensitive) {
term = term.toLowerCase();
}
const terms = term.split(" ");
if (!keys) {
return terms.every(term => searchStrings([item], term, options));
}
if (typeof keys === "string") {
keys = [keys];
}
return terms.every(term => {
// allow search in specific fields with the syntax `field:search`
let currentKeys;
if (term.indexOf(":") !== -1) {
const searchedField = term.split(":")[0];
term = term.split(":")[1];
currentKeys = keys.filter(key => key.toLowerCase().indexOf(searchedField) > -1);
} else {
currentKeys = keys;
}
return currentKeys.some(key => {
const values = getValuesForKey(key, item);
values[0] = toTrLowerCase(values[0]);
return searchStrings(values, term, options);
});
});
};
}
And then i added fuse.js to package.json.
"fuse.js": "^3.0.0"
I called createFilter function like that... term is searching value key
keysToFilter is which array column you wanna search.
this.state.allData.filter(createFilter(term, this.state.keysToFilter));
Link: https://github.com/enkidevs/react-search-input

How to wrap FileSystemFileEntry.file into a function that returns Observable?

I'm trying to read files recursively from the dropped folder.
onDrop(event) {
event.preventDefault();
this.folderData = [];
this.filesData = [];
const items = event.dataTransfer.items;
for (let i = 0; i < items.length; i++) {
const item = items[i].webkitGetAsEntry();
if (item) {
this.scanFiles(item, this.folderData);
}
}
// send out data
// this.dropped.emit({ folderData: this.folderData, filesData: this.filesData });
}
private scanFiles(item, container: Array<any>) {
const nodeData = {
name: item.name,
isDirectory: item.isDirectory,
item: item,
children: []
};
container.push(nodeData);
if (item.isDirectory) {
const directoryReader = item.createReader();
directoryReader.readEntries(entries => {
if (entries) {
entries.forEach(entry => this.scanFiles(entry, nodeData.children));
}
});
} else if (item.isFile) {
// How to return an Observable array here?
item.file(file => {
file.fullPath = item.fullPath;
this.filesData.push(file);
});
}
}
According to MDN, FileSystemFileEntry.file returns the result in its callback.
https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry/file
So before onDrop sends out the result, it has to wait, until all the FileSystemFileEntry.file callbacks complete.
I want to use Observable.forkJoin to achieve this. But before that, how to wrap FileSystemFileEntry.file into a function that returns Observable?
It turns out that a single forkJoin can't resolve the problem. I finally complete this by recursive forkJoin.
onDrop(event) {
event.preventDefault();
this.folderData = [];
this.filesData = [];
const items = event.dataTransfer.items;
const obs = [];
for (let i = 0; i < items.length; i++) {
const item = items[i].webkitGetAsEntry();
if (item) {
obs.push(new Observable<any>(observer => this.scanFiles(item, this.folderData, observer)));
}
}
forkJoin(obs).subscribe(e => {
this.dropped.emit({ folderData: this.folderData, filesData: this.filesData });
});
}
private scanFiles(item, container: Array<any>, observer: Subscriber<any>) {
const nodeData = {
name: item.name,
isDirectory: item.isDirectory,
item: item,
children: []
};
container.push(nodeData);
if (item.isDirectory) {
const directoryReader = item.createReader();
directoryReader.readEntries(entries => {
if (entries) {
if (entries.length === 0) {
observer.next();
observer.complete();
} else {
const subObs = entries.map(entry => new Observable<any>(innerObserver =>
this.scanFiles(entry, nodeData.children, innerObserver)));
forkJoin(subObs).subscribe(e => {
observer.next();
observer.complete();
});
}
} else {
observer.next();
observer.complete();
}
});
} else if (item.isFile) {
item.file(file => {
file.fullPath = item.fullPath;
this.filesData.push(file);
observer.next();
observer.complete();
});
}
}
For more detial check https://github.com/ft115637850/ngx-folder-uploader.

Categories