Refactor Javascript ES6 - javascript

i need help with refactoring below block of code. I was asked to avoid using let and to use const, how can i use constant here as i need to return all the options having possible match id.
const findRecordExists = (options, possibleMatchId) => {
let item;
options.forEach(option => {
option.waivers.forEach(waiver => {
if (waiver.waiverNameId === possibleMatchId) {
item = option;
}
});
});
return item;
};
Example of options would be :
options: [{
name:Abc
waivers: [ {waiverNameId :1}, {waiverNameId:2} ]
}]

Use filter to iterate over the options array, returning whether .some of the waiverNameIds match:
const findRecordExists = (options, possibleMatchId) => {
return options.filter(
({ waivers }) => waivers.some(
({ waiverNameId }) => waiverNameId === possibleMatchId
)
);
};
Or, if you don't like destructuring:
const findRecordExists = (options, possibleMatchId) => {
return options.filter(
option => option.waivers.some(
wavier => wavier.waiverNameId => waiverNameId === possibleMatchId
)
);
};
Since the result is being immediately returned from the findRecordExists function, there isn't even any need for an intermediate item (or items) variable.

That's okay.
Using const to declare an identifier only makes the value of the identifier unchangeable if the value of the identifier is a JavaScript primitive e.g a number or a boolean.
If the value of the identifier is an object or an array (an array is a type of object in JavaScript), using const to declare it doesn't mean that the value of that object identifier cannot be changes. It only means that the identifier cannot be reassigned.
To refactor your code using const, use the code listing below
const findRecordExists = (options, possibleMatchId) => {
const optionsWithPossibleMatches = [];
options.forEach(option => {
option.waivers.forEach(waiver => {
if (waiver.waiverNameId === possibleMatchId) {
optionsWithPossibleMatches.push(option);
}
});
});
return optionsWithPossibleMatches;
};
If you want to skip intermediate steps of creating variables to store each option that matches your condition, you can use the filter method as prescribed by #CertainPerformance

You can re-factor with using find method. This will simplify and avoids the item variable.
const options = [
{
name: "Abc",
waivers: [{ waiverNameId: 1 }, { waiverNameId: 2 }]
}
];
const findRecordExists = (options, possibleMatchId) =>
options.find(option =>
option.waivers.find(waiver => waiver.waiverNameId === possibleMatchId)
);
console.log(findRecordExists(options, 2));
console.log(findRecordExists(options, 3));

Related

Javascript Function: Use Variable instead of single Values

I found here a script. It works fine. But now, I want to use a Variable instead of single values.
Here the original script:
const customData = {
"func":"bri",
"oid":"ID",
"onVal":1,
"offVal":0,
"...":"..."
}
const getSubset = (obj, ...keys) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
const Light.bri = getSubset(customData, "oid", "onVal", "offVal");
Result (OK):
bri: {
offVal: 0,
oid: "objekt-ID",
onVal: 1
},
Now I want to do define the keys in a variable, ideally as a object. But this do not work.
const params = {bri: "oid, onVal, offVal"};
const Light.bri = getSubset(customData, params.bri);
Result (NOK):
bri: {
oid, onVal, offVal: undefined
},
description: "Deckenspots"
}
what changes do I have to make?
Define the bri property as an array of strings. That way you can use the spread syntax (...) to pass the strings as individual arguments.
const params = {bri: ["oid", "onVal", "offVal"]}; // bri is now an array.
const Light.bri = getSubset(customData, ...params.bri); // Notice the ... before params.bri

Smarter way of using filter and map instead of filter and loop

I want to create a smarter way of coding of the following example. Important is that each loop (for activeFilters) needs to be fully done, before we want to return the filtersTest.
const createFilters = async () => {
const filtersTest = [] as any
// Only create active filters by checking count.
const activeFilters = getComponentFilter.value.filter(function(item) {
if (item.items) {
return item.items.some((obj) => obj.count)
}
});
// Loop through the active filters and push arrays into the object.
for(let i = 0 ; i < activeFilters.length; i++) {
const options = await createFilterOptions(activeFilters[i].id, activeFilters[i].items);
const array = {
defaultValue: null,
id: activeFilters[i].id,
value: 'nee',
label: activeFilters[i].label,
options: options,
}
filtersTest.push(array)
}
return filtersTest;
}
First of all, it should be clear that createFilters is not going to return the array, but a promise that will eventually resolve to that array.
With that in mind, you can reduce your code a bit, using Promise.all, the ?. operator, destructuring parameters, and shorthand property names in object literals:
const createFilters = () => Promise.all(
getComponentFilter.value.filter(({items}) =>
items?.some((obj) => obj.count)
).map(({id, label, items}) =>
createFilterOptions(id, items).then(options => ({
defaultValue: null,
id,
value: 'nee',
label,
options
}))
)
);

How to simplify function which returns an object?

I have a function which returns an object but I don't like that I gotta declare it first and then do forEach method
export default (data) => {
const keysWithDotsObject = {};
Object.keys(data).forEach((keyWithDot) => {
Object.keys(data[keyWithDot]).forEach((key) => {
keysWithDotsObject[`${keyWithDot}.${key}`] = data[keyWithDot][key];
});
});
return keysWithDotsObject;
};
I think there should be something like this
export default (data) => {
const keysWithDotsObject = Object.keys(data).map((keyWithDot) => {
Object.keys(data[keyWithDot]).map((key) => ({
[`${keyWithDot}.${key}`]: data[keyWithDot][key],
}));
});
return keysWithDotsObject;
};
But for some reason, it doesn't work.
PS: In this part --
[`${keyWithDot}.${key}`]
-- I'm trying to create a key with a name separated by a dot (I don't like that, but that's what back-end wants me to)
Input :
Query1 = {
locus_ids: [25, 26],
microorganism_ids: [12],
};
Output :
Query1.locus_ids: [25, 26],
Query1.microorganism_ids: [12]
I also would like any suggestions on how to write more readable code
Did you consider using reduce?
export default (data) => Object.keys(data).reduce((acc, keyWithDot) => (
Object.keys(data[keyWithDot]).forEach((key) => {
acc[`${keyWithDot}.${key}`] = data[keyWithDot][key];
}),
acc
), {});
You can also use Object.fromEntries, map and flatMap should do the job:
export default (data) =>
Object.fromEntries(
Object.keys(data).flatMap((keyWithDot) =>
Object.keys(data[keyWithDot]).map((key) => [`${keyWithDot}.${key}`, data[keyWithDot][key]])
)
);
First, you build an array for each subentry, for each subentry, you flatten the array you got into an array of key/value, then with Object.fromEntries, you make a new object!
What if the backend decides to add one more nesting? I would choose to go with a recursive function that accounts for that:
function flattenObject(data) {
return Object.fromEntries(
Object.entries(data).flatMap(([key, value]) => {
if (Array.isArray(value) || typeof value !== 'object') {
// The condition might need to be changed depending on the expected data types
return [[key, value]];
}
return Object.entries(flattenObject(value))
.map(([suffix, nestedValue]) => [`${key}.${suffix}`, nestedValue]);
})
)
}
This works even for inputs such as:
{
query1: {
nested: {
test: true
}
},
query2: [1, 2, 3]
}
The above example results in:
{
"query1.nested.test": true,
"query2": [1,2,3]
}

Optimize repeated expensive computations in Rxjs given an Observable of Array

Given the following script:
type Data = {
id: string
value: number
}
// This is a pure function: given val, res will always be the same
const veryExpensiveCalc = (val: number) => {
const res = // ... 5 seconds of sync computation ...
return res
}
const array$ = new ReplaySubject<Array<Data>>(1)
const allComputedValues$ = array$.pipe(
map(arr =>
arr.map(item =>
// I want this veryExpensiveCalc to be performed only when
// item.value changes for this item.id, or the item was not
// there before
veryExpensiveCalc(item.value)
)
)
)
allComputedValues$
.pipe(
tap(newStruct => {
console.log('newStruct', newStruct)
})
)
.subscribe()
I'd like to optimize how allComputedValues$ is calculated. In particular let's say that an item is added or removed to array$, or the order of the elements change: then veryExpensiveCalc is executed again on every item of the array, even though that's not needed at all. I just need allComputedValues$ from the previous computation with the result of veryExpensiveCalc applied only to the newly added element (in case of addition).
What's the best way to write this in a functional reactive fashion? It should work also if there are multiple elements edited/added/removed at the same time (so that veryExpensiveCalc is executed only on the elements that have a different value given the same id).
I think you can use the scan() operator here:
const allComputedValues$ = array$.pipe(
scan(
(acc, crtArr) => {
// with these we 'automatically' remove the current items(from `acc`) whose ids are not in the current array
const newIds = {};
const newValues = {};
crtArr.forEach(crt => {
const { id: currentId, value: crtValue } = crt;
// if new or edited
if (!acc.ids[currentId] || acc.values[currentId] !== crtValue) {
newIds[currentId] = true;
newValues[currentId] = veryExpensiveCalc(crtValue);
}
});
return { ids: newIds, values: newValues };
},
{ ids: {}, values: {} },
),
)

es6 calling a function in map loop

The following code works fine.
function getProducts(params) {
return params.productQuantities
.map(prod => ({
purchaseOrderLine: null,
haulerCostCode: getOrderLine(params, prod).haulCostCode,
productCostCode: getOrderLine(params, prod).productCostCode,
typeOfWork: getOrderLine(params, prod).productCostCode,
}))
.reduce((accumulator, currentValue) => {
accumulator.push(currentValue);
return accumulator;
}, []);
}
function getOrderLine(params, ticketLine) {
return params.orderDetail.order.orderLineItems
.find(orderLine => orderLine.id == ticketLine.id);
}
My question is how do I avoid multiple calls to getOrderLine()?
use a function body instead of a function expression:
.map(prod => {
const o = getOrderLine(params, prod);
return {
purchaseOrderLine: null,
haulerCostCode: o.haulCostCode,
productCostCode: o.productCostCode,
typeOfWork: o.productCostCode,
}
})
You could use function composition -
const comp = (f, g) =>
x => f(g(x))
const getOrderLine = params => ticketLine =>
params.orderDetail.order.orderLineItems
.find(orderLine => orderLine.id == ticketLine.id)
const makeProduct = orderLine =>
( { purchaseOrderLine: null
, haulerCostCode: orderLine.haulCostCode
, productCostCode: orderLine.productCostCode
, typeOfWork: orderLine.productCostCode
}
)
const getProducts = params =>
params.productQuantities
.map(comp(makeProduct, getOrderLine(params)))
I removed the reduce bit because it's doesn't make any sense. map already creates a new array.
There's other serious problems here though. These functions are digging into object properties sometimes three levels deep. This creates a tight coupling in you code base. See Law of Demeter

Categories

Resources