DRY combine two functions in one [closed] - javascript

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
I am trying to refactor my code and have noticed I am repeating myself. Was wondering if there is a way to combine these two functions in one?
export const fn1 = article => article.categoryValueDtoSet.reduce((res, item) => {
const result = { ...res };
if (item.language) {
const language = item.language.languageValue;
const category = item.categoryValue;
result[language] = category;
}
return result;
}, { it: undefined, de: undefined, en: undefined );
export const fn2 = article => article.titleDtoSet.reduce((res, item) => {
const result = { ...res };
if (item.language) {
const language = item.language.languageValue;
const category = item.titleValue;
result[language] = title;
}
return result;
}, { it: undefined, de: undefined, en: undefined );

First you would need function which you will pass to the reduce function as a callback. For this you can use a higher-order function which is basically a function that returns a function. We can call this function dry
const dry = (fieldName) => (res, item) => {
const result = { ...res };
if (item.language) {
const language = item.language.languageValue;
result[language] = item[fieldName];
}
return result;
}
then we can extract the initial object that is being passed to the reduce callback:
const intialValueObject = { it: undefined, de: undefined, en: undefined };
and then pass the dry function to the reduce function as an argument:
export const fn1 = article => article.categoryValueDtoSet.reduce(dry('categoryValue'), { ...initialValueObject });
export const fn2 = article => article.titleDtoSet.reduce(dry('titleValue'), { ...intialValueObject });

There is pretty much always a way. Could you be more specific with your constraints?
If nothing else, you can share the reduce logic
let reducer = (res, item) => {
const result = { ...res };
if (item.language) {
const language = item.language.languageValue;
const category = item.categoryValue;
result[language] = category;
}
return result;
}
export const fn1 = article => article.categoryValueDtoSet.reduce(reducer, { it: undefined, de: undefined, en: undefined });
export const fn2 = article => article.titleDtoSet.reduce(reducer, { it: undefined, de: undefined, en: undefined });

Most straightforward and simple, albeit with bad naming because I don't know what all this represents:
function reduceIt(it) {
return it.reduce((res, item) => {
const result = { ...res };
if (item.language) {
const language = item.language.languageValue;
const category = item.categoryValue;
result[language] = category;
}
return result;
}, { it: undefined, de: undefined, en: undefined });
}
export const fn1 = article => reduceIt(article.categoryValueDtoSet);
export const fn2 = article => reduceIt(article.titleDtoSet);

Related

How do these nested JavaScript functions work?

I have a question related to this code:
const somaHorasExtras = (salario, valorHorasExtras) => salario + valorHorasExtras;
const calculaDescontos = (salario, descontos) => salario - descontos;
const verifiqueSe = (valor) => {
const assercoes = {
ehExatamenteIgualA(esperado) {
if (valor !== esperado) {
// eslint-disable-next-line no-throw-literal
throw {};
}
},
};
return assercoes;
};
const teste = (titulo, funcaoDeTeste) => {
try {
funcaoDeTeste();
console.log(`${titulo} passou!`);
} catch {
console.error(`${titulo} não passou!!!`);
}
};
teste('somaHorasExtras', () => {
const esperado = 2500;
const retornado = somaHorasExtras(2000, 500);
verifiqueSe(retornado).ehExatamenteIgualA(esperado);
});
teste('calculaDesconto', () => {
const esperado = 2300;
const retornado = calculaDescontos(2500, 200);
verifiqueSe(retornado).ehExatamenteIgualA(esperado);
});
My question is related to the verifiqueSe function specifically. How does this function work? Does someone can explain how this function work in conjunction with the inner function ehExatamenteIgualA? What is the assercoes which is returned?
Thank you.
Your verifiqueSe(valor) function returns an object. You may find it a little confusing, since this syntax:
const foo = {
bar() {
//
}
};
is just a short syntax for object method:
const foo = {
bar: function () {
//
}
};
So in order to call the bar() function, you'd need to reach it through foo:
foo.bar();

Problem pushing a value inside an array located in an object key

Suppose I have the following function:
const createMenu = () => {
const obj = {
consumption: [],
};
return obj;
};
This is a function that, when called, returns the object
{ consumption: [] }
What I am trying to do is create a key inside that object that is a function that, when called with a string parameter, it pushes the string into the array inside the key 'consumption';
Here's my attempt:
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {obj.consumption.push(item); };
obj.order = order;
return obj;
};
The expected result is that, when calling that function inside the object with a string parameter,like this:
createMenu().order('pizza');
when I run:
console.log(createMenu().consumption);
my result is:
['pizza']
but it is not working. I would appreciate if anyone could help me with this.
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
};
obj.order = order;
return obj;
};
createMenu().order('pizza');
console.log(createMenu().consumption);
You creating two instance of createMenu,
Your likely wanting to create 1
const menu = createMenu()
Also if you want to chain the functions, you will want to return the obj again inside order.
Below is an example..
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
return obj;
};
obj.order = order;
return obj;
};
const menu = createMenu().order('pizza');
console.log(menu.consumption);
You have to store the object created by createMenu() to a variable then perform operations to that variable. The update below should work.
In your code, you've created a new object when called the createMenu() function at the console.log. Which is not what you are wanting.
const createMenu = () => {
const obj = {
consumption: [],
};
let order = (item) => {
obj.consumption.push(item);
};
obj.order = order;
return obj;
};
const menu = createMenu();
menu.order('pizza');
menu.order('burger');
console.log(menu.consumption); // ["pizza", "burger"]
.as-console-wrapper{min-height: 100% !important; top: 0}
You may want to consider a class for this. Set up the array in the constructor, add a method to update the array when a new menu item is introduced, and have a final method that returns your desired object.
class CreateMenu {
constructor() {
this.consumption = [];
};
orderItem(item) {
this.consumption.push(item);
return this;
}
getList() {
return { consumption: this.consumption };
}
};
const menu = new CreateMenu();
const order = menu
.orderItem('pizza')
.orderItem('cheese sticks')
.getList();
console.log(order);

Javascript - Using compose with reduce

I am learning functional programming with javascript. I have learned that 2 parameters are needed for reduce. Accumalator and the actual value and if we don't supply the initial value, the first argument is used. but I can't understand how the purchaseItem functions is working in the code below. can anyone please explain.
const user = {
name: 'Lachi',
active: true,
cart: [],
purchases: []
}
let history = []
const compose = (f, g) => (...args) => f(g(...args))
console.log(purchaseItem(
emptyCart,
buyItem,
applyTaxToItems,
addItemToCart
)(user, {name: 'laptop', price: 200}))
function purchaseItem(...fns) {
console.log(fns)
return fns.reduce(compose)
}
function addItemToCart (user, item) {
history.push(user)
const updatedCart = user.cart.concat(item)
return Object.assign({}, user, { cart: updatedCart })
}
function applyTaxToItems(user) {
history.push(user)
const {cart} = user
const taxRate = 1.3
const updatedCart = cart.map(item => {
return {
name: item.name,
price: item.price * taxRate
}
})
return Object.assign({}, user, { cart: updatedCart })
}
function buyItem(user) {
history.push(user)
return Object.assign({}, user, { purchases: user.cart })
}
function emptyCart(user) {
history.push(user)
return Object.assign({}, user, {cart: []})
}
Maybe it helps if you take a minimal working example and visualize the output structure:
const comp = (f, g) => x => f(g(x));
const inc = x => `inc(${x})`;
const sqr = x => `sqr(${x})`;
const id = x => `id(${x})`;
const main = [sqr, inc, inc, inc].reduce(comp, id);
console.log(main(0)); // id(sqr(inc(inc(inc(0)))))
Please note that we need id to allow redicung an empty array.
It's a way of creating a pipeline of functions whereby the output from one function is used as the parameter of the next, so we end up with a composed function that is effectively
(...args) =>
emptyCart(
buyItem(
applyTaxToItems(
addItemToCart(...args)
)
)
)
Writing the reduce out in longhand might help in understanding:
fns.reduce((acc, currentFn) => compose(acc, currentFn))

what am i doing wrong as my code is not giving expected results

Question
displayCartTotal, use the formatAsMoney function to set appState.billFormated to the formatted total bill. The already assigned appState.bill and appState.country should come be handy now!
Set the text content of the data-bill SPAN to the formatted bill set in appState.billFormated
Finally, call uiCanInteract to wrap up displayCartTotal.
!!
my code
> Blockquote
const formatAsMoney = (amount, buyerCountry) => {
const country = countries.find(c => c.country === buyerCountry);
const {code, currency} = country;
if (code && currency) return amount.toLocaleString(code, {style: "currency", currency});
return amount.toLocaleString("en-us", {style: "currency", currency: "USD"});
};
const detectCardType = ({target}) => {
}
const validateCardExpiryDate = ({target}) => {
};
const validateCardHolderName = ({target}) => {
};
const uiCanInteract = () => {
};
const displayCartTotal = ({results}) => {
const {results} = results;
const [data] = results;
const {itemsInCart, buyerCountry} = data;
appState.items = itemsInCart;
appState.country = buyerCountry;
appState.bill = itemsInCart.reduce((result, item,) => result = item.price * item.qty, 0);
appState.billFormated = formatAsMoney (appState.bill, appState.country);
databill.textContent = appstate.billFormated;
uiCanInteract();
};
want pointers to how to effectively solve the above posted question please!!
This is probably what you meant
const displayCartTotal = (resultingStuff) => {
const {results} = resultingStuff;
const [data] = results;
const {itemsInCart, buyerCountry} = data;
appState.items = itemsInCart;
appState.country = buyerCountry;
appState.bill = itemsInCart.reduce((result, item,) => result = item.price * item.qty, 0);
appState.billFormated = formatAsMoney (appState.bill, appState.country);
databill.textContent = appstate.billFormated;
uiCanInteract();
};
const blah = ({result}) => {
const {result} = result
document.write(result)
}
blah({result: { result: 'apple' } })
/*
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'result'.
*/
SyntaxError: Unexpected token '}'. Cannot declare a lexical variable twice: 'result'.
You have defined results 2x in the same lexical scope and JavaScript (nor anyone reading this) have any idea which results you refer to.
Note that in general: it is unwise to use such vague variable names as results and data what type of data? what types of results this will also help you from accidentally naming the parameter the same as one of variables that you attempted to assign:
Note that:
const displayCartTotal = ({results}) =>
Is equivalent to:
const displayCartTotal = (stuff) => {
const results = stuff.results
meaning you already assigned results... also are you sure that the object really has results in it twice?
because here is what your code did:
const displayCartTotal = (stuff) => {
const results = stuff.results
const results = results.results // UNHAPPY: cannot declare lexical variable twice
If the api really replies with:
{
"results": {
"results": {
"data": []
}
}
then your best bet is to do:
const displayCartTotal = ({results}) => {
const { results: innerResults } = results
const { data } = innerResults
or you can do the above in one line:
const displayCartTotal = ({results: { results: { data } }) => {
// Now only data will be defined but it looks like that's all you need.

Best way to write a function that either returns a value, or a resolved promise

So I am trying to write a logging HOC that will take a function and log the result of that function. I would like this hoc to be able to log the result of any function whether that function returns a promise or a value. This is what I have so far:
const logFn = (x) => {
return (...args) => {
const result = x(...args);
console.log(`result: ${result}`);
return result;
}
};
I would like to have this function handle the case when x returns a promise. I know a hacky way to do it (typeof result === object && typeof result.then === function) but this seems brittle. I am nearly sure there is a more elegant way to do this but I am struggling to find it.
I have included a failing jest test below:
import logFn from './logFn';
describe('logFn', () => {
let outputData;
beforeEach(() => {
const storeLog = inputs => (outputData += inputs);
console["log"] = jest.fn(storeLog);
require('./logFn');
outputData = ""
});
it('handles async functions', () => {
const add2P = (x, y) => Promise.resolve(x + y);
const logAdd2 = logFn(add2P);
const expected = add2P(1,2).then((data) => data);
const actual = logAdd2(1,2);
expect(outputData).toBe('result: 3');
expect(actual).toEqual(expected);
})
});
bonus points if you can help me clean up the beforeEach.
This has the unfortunate side effect of not logging synchronously, but you could try this.
const logFn = (x) => {
return (...args) => {
const result = x(...args);
Promise.resolve(result).then(function(value) {
console.log(`result: ${value}`);
})
return result;
}
};
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
While this answer isn't much different from #lemieuxster's answer, there is one major difference.
const logFn = fn => function () {
const result = fn.apply(this, arguments);
Promise.resolve(result).then(value => { console.log(`result: ${value}`); });
return result;
};
This preserves the calling context if, for example, you want to logify member methods:
const logFn = fn => function () {
const result = fn.apply(this, arguments);
Promise.resolve(result).then(value => { console.log(`result: ${value}`); });
return result;
};
const foo = {
bar: 'Hello, world!',
doSomething: logFn(function () {
return this.bar;
})
};
foo.doSomething();

Categories