Simplify forEach in forEach React - javascript

I have a function where I have to return for each "subcontractor" its response for each selection criteria.
Subcontractor object contains a selectionCriteria object. selectionCriteria object contains an array of data for each selectionCriteria a user has responded to.
Each array item is an object, that contains files, id, request (object that contains info about selection criteria user is responding to), response (contains value of the response).
Here is an example of how a subcontractor looks:
This is the function I come up with, but it's quite complex:
const { subcontractors } = useLoaderData<typeof loader>();
const { t } = useTranslation();
const submittedSubcontractors = subcontractors.filter(
(s) => s.status === 'submitted'
);
const subcontractorsResponsesToSelectionCriteria: Array<ISubcontractor> = [];
let providedAnswersResponded: boolean | null = null;
let providedAnswersFiles: Array<IFile> | [] = [];
let providedAnswersRequiresFiles: boolean | null = null;
submittedSubcontractors.forEach((u) => {
u.selectionCriteria.forEach((c) => {
if (c.request.id === criteriaId) {
if (c.response && 'answer' in c.response) {
if (typeof c.response.answer === 'boolean') {
providedAnswersResponded = c.response.answer;
} else {
providedAnswersResponded = null;
}
} else {
providedAnswersResponded = null;
}
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u as ISubcontractor);
}
});
});
How could I simplify this code by using .reduce() method, or maybe even better ideas?

You should start working on reducing the level of nesting in your if/else like so:
function getProvidedAnswersResponded(response: any) {
if (response && ('answer' in response) && (typeof response.answer === 'boolean')) {
return response.answer;
}
return null;
}
submittedSubcontractors.forEach(u => {
u.selectionCriteria.forEach(c => {
if (c.request.id !== criteriaId) {
return;
}
providedAnswersResponded = getProvidedAnswersResponded(c.response);
providedAnswersFiles = c.files;
providedAnswersRequiresFiles = c.request.are_files_required;
subcontractorsResponsesToSelectionCriteria.push(u);
});
});
The strategy followed was basically to invert the special cases (such as c.requet.id === criteriaId) and exit the function immediately.
Also, extracting the "provided answer responded" function seems atomic enough to move it to a separate block, giving it more verbosity about what that specific code block is doing.

Related

React filters products in a one go

I just came from the interview, I have implemented multiple filter feature in my assignment.
assignment is live here: https://tooth-store.netlify.app/
here is my code where I am filtering with according to the value of filters state.
const filterData = () => {
let data = [...products];
if (byCategory !== 'all') {
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all') {
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != '') {
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};
Interviewer told me if we will be having a lot of products then going with this approach is not a good idea, so we have to filter at a one go not for all single value of filters.
example: category filter is applied earlier, and now we are changing the rating then it will again filters the category first. so filter it in a one go.
Can anyone explain me in a detail how I have to deal with this, I got blank at that time, but now i am guessing i simply have to check for all filters values with && operator in a single filter
Is there is any other best way?
You can make a common function, where you can check the category against data, here I give you an example in the below code, I make a common function where I get a two param, one is item this is list of items and the second is category user which category against wants data, In the function, I define the some categories array, you can store the categories dynamically in the state and then check if category exists in the categories array, If exists then you can filter the data against the categories list and If you want to see the full example CLICK HERE.
const func = (item, category) => {
let newArr = [];
if (category !== "all") {
const categories = ["electronics", "men's clothing", "jewelery"];
const ratings = [1.9, 2.5, 6.7];
if (categories.includes(category)) {
newArr = item.category.toLowerCase() === category.toLowerCase();
} else if (ratings.includes(category)) {
newArr = Math.floor(item.rating.rate) === category;
} else {
newArr = item.title.toLowerCase().includes(category.toLowerCase());
}
}
return newArr;
};
const filterData = () => {
let data = [...products];
data = data.filter((item) => {
return func(item, byCategory);
});
return data;
};
you can use this method where your filter option will run for only one type at a time just you have to pass a parameter like this
const filterData = (type) => {
let data = [...products];
if (byCategory !== 'all' && type == 'cat') { // call this filterData('cat')
data = data.filter((item) => {
return item.category.toLowerCase() === byCategory.toLowerCase();
});
}
if (byRating !== 'all'&& type == 'rate') { // call this filterData('rate')
data = data.filter((item) => Math.floor(item.rating.rate) == byRating);
}
if (bySearch != ''&& type == 'search') { // call function by filterData('search')
data = data.filter((item) =>
item.title.toLowerCase().includes(bySearch.toLowerCase())
);
}
return data;
};

Function declared in a loop contains unsafe references to variable(s)

My code checks if a user is available. See snippet below:
const users = ['user1', 'user2', 'user3', 'user4']
const usersToAdd = 2
const getRandomWorker = (userArray) => {
return userArray[Math.floor(userArray.length * Math.random())]
}
const availableUsers = []
for (let i = 0; i < usersToAdd; i += i) {
let randomWorker = getRandomWorker(users)
let didAddWorker = false
while (!didAddWorker) {
if (checkIfUserAvailable(randomWorker)) {
availableUsers.push(randomWorker)
users = users.filter((user) => user !== randomWorker)
didAddWorker = true
} else if (!users.length) {
didAddWorker = true
} else {
users = users.filter((user) => user !== randomWorker)
}
}
}
My only problem is that it contains unsafe references to variables(s) because I get the following error:
Function declared in a loop contains unsafe references to variable(s) randomWorker.
I've searched around and fiddled with my code but I can't get rid of the error. I don't know where to look anymore.
As T.J Crowder suggested I had to make a function outside of the loop:
const filterOut = (array, target) => array.filter(element => element !== target);
That fixed it. A bit strange but I can continue know. Thanks!

replace lines between two lines in a template file in nodejs

I have a file with the following content:
function(doc) {
//pr reqs
var facet = true;
var store = true;
// Template start
var fields = {
}
// template end
noiseList = ["type", "objects", "value"]
const isNumeric = (num) => {
return !isNaN(num)
}
const emitIndex = () => {
if (doc.created_by_ref) {
Object.keys(fields).forEach(function(key) {
if (typeof fields[key] == 'object' && fields[key].length !== undefined) {
if (fields[key].length === 0) {
index(key, 'UNDEFINED', {
'store': store,
'facet': facet
});
} else {
fields[key].forEach(function(ele) {
index(key, ele.toString(), {
'store': store,
'facet': facet
});
})
}
} else {
index(key, fields[key].toString(), {
'store': store,
'facet': facet
});
}
})
}
}
Object.keys(doc).map(obj => {
if (typeof doc[obj] === 'object' && doc[obj] !== null) {
traverseObjectsInDoc(doc[obj], noiseCancelation(obj) ? "" : obj, doc.objects, false);
} else if (doc[obj] !== null && isValueType(obj) && !noiseCancelation(obj)) {
AddToFields(`${obj}`, doc[obj])
}
});
emitIndex();
}
As you I have two special sign there: template start and template end
what I am trying to achieve is to replace sth similar to
var fields = {
"test1": "test",
"test2": "test2"
}
instead of
var fields = {
}
in that file. And I should mention that this fields are generated in runtime so the content needs to be dynamic as well that is why I need this approach. All I can think of is to read the file
const searchAll1 = () => {
const contents = fs
.readFileSync("./lib/design_documents/searchAll", "utf8");
// find the template end and start replace the new fields some and return
};
and find the template end and start replace the new fields somehow and return. However I am not really sure if this is the best way?
What is the best way to do so?
Adjust the design of your function by adding a second parameter you can add the fields object dynamically whenever you call the function.
// your-script.js
module.exports = function(doc, fields) {
...
}
Then when you import and use the function, create a new object and pass it to your function and call it.
const yourFunction = require('./your-script.js');
let doc = someValue;
let fields = {
"test1": "test",
"test2": "test2"
}
yourFunction(doc, fields);

Return from inside an observable

I'm think fundamentally i'm doing something wrong. I'm trying to find a single entity by pulling it from filtered list, if there isn't an entity there I need to create one.
I feel like this function is wrong because I should be returning an observable but instead returning nothing
getOrCreateNew(receiverId: number) : Observable<Conversation> {
var userId = this.identity.userInfo.id;
//TODO: you should be using an expression here, and having a builder for generating your
//filters in case you want to switch them in the future
var employerFilter = new PropertyFilterNode("EmployerUserId", FilterCondition.Equal, receiverId.toString());
var employeeFilter = new PropertyFilterNode("EmployeeUserId", FilterCondition.Equal, userId.toString());
let conversationFilter = new BinaryFilterNode(employerFilter, employeeFilter, Combiner.Or);
this.getList(conversationFilter).subscribe(entities => {
if (entities == null || entities.length == 0) {
let conversation: Conversation;
conversation.employerUserId = receiverId;
conversation.employeeUserId = userId;
return this.create(conversation);
}
else {
let entity = entities[0];
return Observable.of(entity); //.Return(entity)
}
});
return null;
}
How can I return an observable which is return from inside subscribe?
The way getList subscription works suggests that it is supposed to be mergeMap or switchMap (considering that create returns an observable as well):
return this.getList(conversationFilter).mergeMap(entities => {
if (entities == null || entities.length == 0) {
let conversation: Conversation;
conversation.employerUserId = receiverId;
conversation.employeeUserId = userId;
return this.create(conversation);
}
else {
let entity = entities[0];
return Observable.of(entity); //.Return(entity)
}
});
In this case an observable that is returned from getOrCreateNew should be subscribed in order to emit values because it isn't subscribed internally.
Place the return before this.getList also :
return this.getList(conversationFilter).subscribe(entities => {

How can I use a Javascript object/map as queue

Right now I have a queue (JS array) that is used to store players waiting for a game. I need the FIFO property of a queue so that players who were added to the queue first, get put in a new game first. The problem with a queue is that it doesnt have constant time lookup. It would be great if I could have a map that kept track of the order of insertion (i know that relying on a map to do this is JS is not reliable). If I give the property a value for its insertion order, it would need to be updated if someone leaves the queue, so that isnt helpful either. Anyway around this? A way to get constant lookup and maintain insertion order?
If you don't have memory constraints, maybe you can maintain a map with the queue implemented as a double linked list. Here is a sample implementation:
function Queue() {
var oldestRequest,
newestRequest,
map = {};
this.addUser = function(userID) {
var newRequest = { userID: userID };
map[userID] = newRequest;
// Set this as the oldest request if it is the first request
if (!oldestRequest) {
oldestRequest = newRequest;
}
// If this isn't the first request, add it to the end of the list
if (newestRequest) {
newestRequest.next = newRequest;
newRequest.previous = newestRequest;
}
newestRequest = newRequest;
};
this.nextUser = function() {
// If we don't have any requests, undefined is returned
if (oldestRequest) {
var request = oldestRequest;
oldestRequest = request.next;
delete map[request.userID];
// Make sure we don't hang on to references to users
// that are out of the queue
if (oldestRequest) {
delete oldestRequest.previous;
}
// This is the last request in the queue so "empty" it
if (request === newestRequest) {
newestRequest = undefined;
}
return request;
}
};
this.removeUser = function(userID) {
var request = map[userID];
delete map[userID];
if (request.previous) {
request.previous.next = request.next;
}
if (request.next) {
request.next.previous = request.previous;
}
};
return this;
}
You can use a map together with a queue to provide constant time access. Below is the implementation in TypeScript 4.2. Map is used instead of Object to provide better performance in addition and removal of values.
// TypeScript typing
export type KeyValuePair<K, V> = [ K, V ]
interface ValueData<V> {
value: V
refCount: number
}
// Public classes
export class MapQueue<K, V> {
readonly #queue: Array<KeyValuePair<K, V>>
readonly #map: Map<K, ValueData<V>>
constructor () {
this.#queue = []
this.#map = new Map()
}
get length (): number {
return this.#queue.length
}
unshiftOne (pair: KeyValuePair<K, V>): number {
const [key, value] = pair
const valueData = this.#map.get(key)
if (valueData !== undefined) {
if (valueData.value !== value) {
throw new Error(`Key ${String(key)} with different value already exists`)
}
valueData.refCount++
} else {
this.#map.set(key, {
value,
refCount: 1
})
}
return this.#queue.unshift(pair)
}
pop (): KeyValuePair<K, V> | undefined {
const result = this.#queue.pop()
if (result !== undefined) {
const valueData = this.#map.get(result[0])
if (valueData !== undefined) {
valueData.refCount--
if (valueData.refCount === 0) {
this.#map.delete(result[0])
}
}
}
return result
}
get (key: K): V | undefined {
return this.#map.get(key)?.value
}
}

Categories