Repeat method chain over for loop - javascript

I'm looking to repeat and stack on methods to a chain depending on length x;
const thing = ObjWithMethods
thing
.something()
.somethingElse()
.repeat(() => {
for (let i = 0; i < x; i++) {
thing.repeatedMethod()
}
})
.endMethods();
Is there a way to do this?
Edit:
This is the current concrete example I have
import HummusRecipe from 'hummus-recipe';
const getPageCount = require('docx-pdf-pagecount');
const generateFooterText = (index, length) => `Page ${index} / ${length}`;
const applyPdfFooter = (input, output, cb) => {
const throwErr = (err) => {cb(err);};
const recipe = new HummusRecipe(input, output, pdfOptions);
getPageCount(input)
.then((pages) => {
console.log('Pages: ', pages);
recipe
.and(function(recipe) {
for (let i = 0; i < pages; i++) {
const text = generateFooterText(i, pages);
recipe
.editPage(i)
.text(text, 0, 0)
.endPage();
}
})
.endPDF();
cb();
})
.catch(throwErr);
};
export default applyPdfFooter;

your recipe could be implemented as:
class Recipe {
constructor(input, output, options) {
this.page = 0;
}
and(fn) {
fn(this);
return this; // chaining
}
editPage(i) {
this.page = i;
return this;
}
text(...args) {
//...
return this;
}
endPage() {
this.page++;
return this;
}
endPdf() { }
}
... but I'm not sure if that and(...) method has any benefit...,

Related

How can I refactor this function call that seems to repeat alot of the same thing over over again?

I have made function to search through blog posts. However, I have to make sure that titles take precedence over excerpt and excerpt over content when added to the containsQuery array. I have made the following code, it seems to be functioning well but seems there are a lot redundant code. How can I improve this?
const findArticles = (posts: any[], query: string) => {
const containsQuery: any[] = []
let words
let i: number
if (query.includes(' ')) {
words = query.toLowerCase().split(' ')
}
const findArticle = (multipleWords:boolean, posts: any[], query:string, searchedParam:string, words:string[] = [],) => {
if (multipleWords === false) {
for (i = 0; i < posts.length; i++) {
if (posts[i][`${searchedParam}`].toLowerCase().includes(query.toLowerCase())) {
containsQuery.push(posts[i])
}
}
} else {
for (i = 0; i < posts.length; i++) {
if(words.every(q => posts[i][`${searchedParam}`].toLowerCase().includes(q))) {
containsQuery.push(posts[i])
}
}
}
}
if (words) {
findArticle(true, posts, query, 'title', words,)
} else {
findArticle(false, posts, query, 'title')
}
if (words) {
findArticle(true, posts, query, 'excerpt', words,)
} else {
findArticle(false, posts, query, 'excerpt')
}
if (words) {
findArticle(true, posts, query, 'content', words,)
} else {
findArticle(false, posts, query, 'content')
}
const oneOfKind = Array.from(new Set(containsQuery.map(article => article.id))).map(id => {
return containsQuery.find(a => a.id === id)
})
return oneOfKind
}
In order to avoid duplicates and save time, I tried to copy the posts in to my own const copyOfPosts = posts and then mutate it as below. But this ended up breaking the code for some reason. Above code seems to be the only way I can make it work right. Any suggestion is most welcome.
if (posts[i][`${searchedParam}`].toLowerCase().includes(query.toLowerCase())) {
containsQuery.push(posts[i])
copyOfPosts.splice(i, 1)
}
Here:
const findArticle = (multipleWords:boolean, posts: any[], query:string, searchedParam:string, words:string[]) => {
const containsQuery: any[] = []
if (multipleWords === false) {
for (let i = 0; i < posts.length; i++) {
if (posts[i][`${searchedParam}`].toLowerCase().includes(query.toLowerCase())) {
containsQuery.push(posts[i])
}
}
} else {
for (let i = 0; i < posts.length; i++) {
if(words.every(q => posts[i][`${searchedParam}`].toLowerCase().includes(q))) {
containsQuery.push(posts[i])
}
}
}
return containsQuery
}
const findArticles = (posts: any[], query: string) => {
let words = query.includes(' ') ? query.toLocaleLowerCase().split(' ') : []
const containsQuery = findArticle(true, posts, query, 'title', words)
containsQuery.push(...findArticle(true, posts, query, 'excerpt', words))
containsQuery.push(...findArticle(true, posts, query, 'content', words))
const oneOfKind = Array.from(new Set(containsQuery.map(article => article.id))).map(id => {
return containsQuery.find(a => a.id === id)
})
return oneOfKind
}
I would refactor findArticle in a better way for me
const findArticle = (multipleWords:boolean, posts: any[], query:string, searchedParam:string, words:string[]) => {
const containsQuery: any[] = []
if (multipleWords === false) {
for (let i = 0; i < posts.length; i++) {
if (posts[i][`${searchedParam}`].toLowerCase().includes(query.toLowerCase())) {
containsQuery.push(posts[i])
}
}
return containsQuery
}
for (let i = 0; i < posts.length; i++) {
if(words.every(q => posts[i][`${searchedParam}`].toLowerCase().includes(q))) {
containsQuery.push(posts[i])
}
}
return containsQuery
}
This is because I do not like use an excessive else like in this case, but people could complain about using two return.

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.

How optimise nested looping in 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);
}
});
}
});

TypeError : .next() is not a function

dataArr = []
let element;
function Users()
{
let url = 'https://randomuser.me/api'
fetch(url).then((response) =>{
return response.json();
}).then((data) =>{
dataArr.push(data['results']);
})
}
for (let i = 0; i < 5; i++) {
Users()
}
console.log(dataArr);
function profileIter(values) {
let nextIndex = 0;
if(nextIndex < values.length){
return {
value: values[nextIndex++],
done: false
}
}
else{
return {
done: true
}
}}
const CVIter = profileIter(dataArr);
console.log(CVIter.next().value);
After running this I got an error saying
CVIter.next() is not a function
Your profileIter function is returning an IteratorResult object, not an iterator, so the next() function is not present.
If you want to get an iterator, you have to make your profileIter a generator like this:
function * profileIter(values) {
for (let nextIndex = 0; nextIndex < values.length; ++nextIndex){
yield values[nextIndex];
}
}
or make your function to return an iterator
function profileIter(values) {
let nextIndex = 0;
return {
next: () => {
if (nextIndex < values.length){
return {
value: values[nextIndex++],
done: false
};
}
return {
done: true
};
},
};
}

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