Use lodash cond to take certain branch - javascript

I'm trying to use lodash cond to invoke different functions provided different inputs. Perhaps I've misunderstood cond, but I really like the clean approach to conditions.
Basically I have a 2-dimensional data array (grid). A header 1-dimensional header array and a 1-dimensional totals array. The user can choose to add data headers and totals to the data array. The total row can be either at the top or bottom.
This turned into eight conditions like this:
const totalAbove = totalPosition >= 1;
const totalBelow = totalPosition <= -1;
const hasLabelsAndTotalsAbove = showLables && totalAbove;
const hasLabelsAndTotalsBelow = showLables && totalBelow;
const noLabelsAndTotalsAbove = !showLables && totalAbove;
const noLabelsAndTotalsBelow = !showLables && totalBelow;
const noTotalHasLabels = showLables && !hasTotalRow;
const noTotalNoLabels = !showLables && !hasTotalRow;
Then I thought I could do the this:
const getData = cond([
[hasLabelsAndTotalsAbove, () => Array.prototype.concat([headers], [totalRow], ...matrixes)],
[hasLabelsAndTotalsBelow, () => Array.prototype.concat([headers], ...matrixes, [totalRow])],
[noLabelsAndTotalsAbove, () => Array.prototype.concat([totalRow], ...matrixes)],
[noLabelsAndTotalsBelow, () => Array.prototype.concat(...matrixes, [totalRow]) ],
[noTotalHasLabels, () => Array.prototype.concat([headers], ...matrixes) ],
[noTotalNoLabels, () => matrixes ]
]);
data = getData();
The above should combine the three arrays into the desired form by concatenating them in the right order. But the result is just undefined. Have I completely misunderstood cond?
For now I've just turned the _cond part into if...else statements, but I find the cond approach cleaner.

You have to use _.matches or other function that allows you to pick a property in some object, with getData you don't have context, because you don't pass anything, and _.cond returns a function that works against an object. If you want to test if hasLabelsAndTotalsAbove is true and execute some logic, you can create an object and pass it to the function returned by _.cond:
const totalPosition = 2;
const showLabels = true;
const hasTotalRow = true;
const totalAbove = totalPosition >= 1;
const totalBelow = totalPosition <= -1;
const definitions = {
hasLabelsAndTotalsAbove: showLabels && totalAbove,
hasLabelsAndTotalsBelow: showLabels && totalBelow,
noLabelsAndTotalsAbove: !showLabels && totalAbove,
noLabelsAndTotalsBelow: !showLabels && totalBelow,
noTotalHasLabels: showLabels && !hasTotalRow,
noTotalNoLabels: !showLabels && !hasTotalRow
};
const m = a => _.matches({ [a]: true });
const getData = _.cond([
[m('hasLabelsAndTotalsAbove'), () => 'Action: hasLabelsAndTotalsAbove'],
[m('hasLabelsAndTotalsBelow'), () => 'Action: hasLabelsAndTotalsBelow'],
[m('noLabelsAndTotalsAbove'), () => 'Action: noLabelsAndTotalsAbove'],
[m('noLabelsAndTotalsBelow'), () => 'Action: noLabelsAndTotalsBelow'],
[m('noTotalHasLabels'), () => 'Action: noTotalHasLabels'],
[m('noTotalNoLabels'), () => 'Action: noTotalNoLabels']
]);
console.log(getData(definitions));
This allows us to select the action to execute if some property of the object evaluates to true.

Related

How to compare 2 object in react JS and get the values

I have 2 state
console.log(this.state.tempWilayahUser)
console.log(this.state.wilayahUser)
how to get row when have same value of id_rt ?
You can use filter() function to achieve this. You can read the docs here.
const state1 = [];
const state2 = [];
const itemsWithIdRtFromState1 = state1.filter(item1 => {
return state2.filter(item2 => item2?.id_rt === item1?.id_rt)
});
const itemsWithIdRtFromState2 = state2.filter(item2 => {
return state1.filter(item1 => item1?.id_rt === item2?.id_rt)
});

React - CheckboxTree filter

So i am using this package "react-checkbox-tree" to make a checkbox, but since this is made on classes components and i need to do it with functions and hooks, this is being a bit tricky to my actual skills.
//Checkbox Tree
const [checkedTree, setCheckedTree] = useState([]);
const [expandedTree, setExpandedTree] = useState(["1"]);
const [filterText, setFilterText] = useState("");
const [nodesFiltered, setNodesFiltered] = useState();
///FILTER LOGIC /////
const onFilterChange = (e) => {
setFilterText(e.target.value);
if (e.target.value) {
filterTree();
}
};
const filterTree = () => {
// Reset nodes back to unfiltered state
if (!filterText || filterText === "" || filterText.length === 0) {
setNodesFiltered(nodes);
return;
}
const nodesFiltered = (nodes) => {
return nodes.reduce(filterNodes, []);
};
setNodesFiltered(nodesFiltered);
};
const filterNodes = (filtered, node) => {
const children = (node.children || []).reduce(filterNodes, []);
if (
// Node's label matches the search string
node.label.toLocaleLowerCase().indexOf(filterText.toLocaleLowerCase()) >
-1 ||
// Or a children has a matching node
children.length
) {
filtered.push({ ...node, ...(children.length && { children }) });
}
return filtered;
};
//
My first problem is that when i search for the parent, i only get the last children of the array for some reason.
The Second is that when i use the backspace button, the filter stops working until i clean every char.
I made a codesandbox to help you guys to understand the problems:
https://codesandbox.io/s/checkboxtree-6gu60
This is the example on with classes:
https://github.com/jakezatecky/react-checkbox-tree/blob/master/examples/src/js/FilterExample.js
Tks in advance!
For your second problem, I solved it by passing through onKeyDown as well as onChange from my search input:
<input
type="text"
onChange={onFilterChange}
onKeyDown={onBackspace}
/>
which calls
// If the user deletes the search terms, reset to unfiltered
const onBackspace = e => {
var key = e.keyCode || e.charCode
// magic numbers are backspace and delete. Naming them didn't work.
if (key == 8 || key == 46) {
setFilterText("")
filterTree()
}
}

Issue with dictionary in React

Relatively new to React/Javascript in general so any help would be appreciated.
I currently have an application which is fetching data for multiple items from an API. buys is a list of dictionaries(called buy here) with fields asset, units and price (among other things).
buys.map(async buy => {
var data = await queryCoinGeckoAPI(buy);
var market_price = data.market_data.current_price.aud;
var price_change = data.market_data.price_change_24h_in_currency.aud;
var price_change_percentage = data.market_data.price_change_percentage_24h_in_currency.aud;
var profit = buy.units === 0 || buy.price === 0 ? 0 : market_price * buy.units - buy.price;
newDictionary[buy.asset] = {
asset: buy.asset,
market_price: market_price,
price_change: price_change,
price_change_percentage: price_change_percentage,
profit: profit
};
});
That's all fine and when I come to log newDictionary:
Hooray it works!
However, the problem comes when I'm not trying to access these values in the dictionary. If I try calling newDictionary['bitcoin'] or Object.keys(newDictionary) or even
for(let key in newDictionary) {
console.log(key);
console.log(newDictionary[key]);
}
for example I get no output.
Undefined returned
Not particularly sure why and couldn't find an answer on this online...
I chose a dictionary because I would like to be able to update my current state (I hope this is how you use the spread operator):
setBuys(
buys.map(b => {
{...b, ...newDictionary[b.asset]};
})
);
Full function in case you need it :
useEffect(() => {
const refreshData = async() => {
var d = await Promise.all(
buys.map(async buy => {
var data = await queryCoinGeckoAPI(buy);
var market_price = data.market_data.current_price.aud;
var price_change = data.market_data.price_change_24h_in_currency.aud;
var price_change_percentage = data.market_data.price_change_percentage_24h_in_currency.aud;
var profit = buy.units === 0 || buy.price === 0 ? 0 : market_price * buy.units - buy.price;
return {
asset: buy.asset,
market_price: market_price,
price_change: price_change,
price_change_percentage: price_change_percentage,
profit: profit
};
})
)
var newDictionary = {};
for (let i = 0; i < d.length; i++) {
newDictionary[d[i].asset] = d[i];
}
console.log(newDictionary);
setBuys(
buys.map(
b => {
{...b, ...newDictionary[b.asset]}
}
)
)
// toast.info('Market updated', {});
}
const interval = setInterval(() => {
refreshData();
}, 60000);
return () => clearInterval(interval);
})
Thanks!
The problem with your final setBuys call is that you're simply not returning anything from the mapper function since it's using {} braces.
You'll want
setBuys(
buys.map(
b => (
{...b, ...newDictionary[b.asset]}
)
)
)
instead (b => (, not b => {).
(I'm not even sure if dictionaries are a thing in React).
You are probably thinking of objects, perhaps that can help you with googling anything related in the future.
I think the spread operator is the culprit here, try replacing it with this (forgive the formatting):
.maps( b => {{b: newArray[b.asset]}}
Try adding to some log statements to see the acual contents of newArray, maybe it is actualy an object instead of an array.

Don't make functions within a loop no-loop-func -React JS

I am trying to find index of array using lodash locationbar. but my react console showing some warnings. can be resolve?
let wishListData = wishList.result;
let j = 0; const jMax = wishListData.length;
for (; j < jMax; j++) {
var index = _.findIndex(products.result, function (product) {
return product.id === wishListData[j]['pid']
});
if (index !== -1) {
products.result[index]['isWishList'] = true;
}
}
Iterate over wishList.result with forEach instead of a for loop, and you'll avoid the warning:
wishListData.forEach(({ pid }) => {
const index = _.findIndex(products.result, ({ id }) => id === pid);
if (index !== -1) {
products.result[index].isWishList = true;
}
});
Note that this is a linter warning, not a Javascript error. Your code works, the linter just considers it to be confusing - better to use array methods instead of loops when possible.
Also, feel free to remove the _ library and just use built-in Javascript methods instead, if you want:
wishListData.forEach(({ pid }) => {
const product = products.result.find(({ id }) => id === pid);
if (product) {
product.isWishList = true;
}
});
Or, for an O(N) solution instead of an O(N^2) solution, figure out all pids first, then iterate over the products:
const pids = new Set(wishListData.map(({ pid }) => pid));
products.result.forEach((product) => {
if (pids.has(product.id)) {
product.isWishList = true;
}
});
You can try something like this as well:
Instead of mutating product in products.result[index]['isWishList'] = true;, you should create new object to minimize side-effect.
Also, instead of looping on wishListData, you can create a list of PIDs and just check index. If this list is created outside, you can create list of PIDs outside as well. That will reduce processing it every time
const wishListPID = wishList.result.map((x) => x.pid);
const result = products.result.map((product) => {
const isWishList = wishListPID.indexOf(product.id) !== -1;
return { ...product, isWishList }
});

Writing if/else statements with 3 conditions with a promise mixed in

So I have this conditional statement with 2 conditions, whereby
let modItemList = this.props.items
if (this.state.searchItemName) { // condition1
modItemList = (
this.props.items.filter(
(item) => item.name.toLowerCase().indexOf(lcName) !== -1 // For name
)
);
} else if (this.state.searchItemAddress) { //condition2
modItemList = (
this.props.items.filter(
(item) => item.fullAddress.some(e => e.toLowerCase().indexOf(lcAddress) !== -1) // For Address
)
);
}
This is where it's a little tricky to explain.
Now I want to add a 3rd condition, which happens only if both condition1 and condition2 are met, AND the outcome is that of executing code from condition1 and condition2.
How would I go about expressing that?
I think you just want to use two separate if conditions where both may run, not if/else if:
let modItemList = this.props.items;
if (this.state.searchItemName) { // condition1
modItemList = modItemList.filter(item =>
item.name.toLowerCase().indexOf(lcName) !== -1 // For name
);
}
if (this.state.searchItemAddress) { //condition2
modItemList = modItemList.filter(item =>
item.fullAddress.some(e => e.toLowerCase().indexOf(lcAddress) !== -1) // For Address
);
}
Nothing is asynchronous here or involves promises. If it did, I would recommend to just place an await in the respective location.
There's no asynchronous action here, so no need to track an async action with a promise.
Probably the simplest thing is to filter the filtered list:
let modItemList = this.props.items;
if (this.state.searchItemName) {
modItemList = modItemList.filter(item => item.name.toLowerCase().includes(lcName));
}
if (this.state.searchItemAddress) {
modItemList = modItemList.filter(item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress)));
}
Or filter once and check for searchItemName and searchItemAddress within the callback:
let modItemList = this.props.items.filter(item =>
(!this.state.searchItemName || item.name.toLowerCase().includes(lcName)) &&
(!this.state.searchItemAddress || item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
Even if the list is in the hundreds of thousands of entries, neither of those is going to be slow enough to worry about.
Or if it really bothers you do do that double-filtering or re-checking, build a filter function:
let modItemList;
let filterFunc = null;
if (this.state.searchItemName && this.state.searchItemAddress) {
filterFunc = item => item.name.toLowerCase().includes(lcName) && item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
} else if (this.state.searchItemName) {
filterFunc = item => item.name.toLowerCase().includes(lcName);
} else if (this.state.searchItemAddress) {
filterFunc = item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
}
modItemList = filterFunc ? this.props.items.filter(filterFunc) : this.props.items;
That involves repeating yourself a bit, though, leaving open the possibility that you'll update one address filter but not the other. You can aggregate the filter functions:
let nameCheck = item => item.name.toLowerCase().includes(lcName);
let addressCheck = item => item.fullAddress.some(e => e.toLowerCase().includes(lcAddress));
let modItemList;
if (this.state.searchItemName && this.state.searchItemAddress) {
modItemList = this.props.items.filter(item => nameCheck(item) && addressCheck(item));
} else if (this.state.searchItemName) {
modItemList = this.props.items.filter(nameCheck);
} else if (this.state.searchItemAddress) {
modItemList = this.props.items.filter(addressCheck(item);
}
If there were more than two, we might look at putting them in an array and doing
modItemList = this.props.items.filter(item => arrayOfFunctions.every(f => f(item)));
So...lots of options. :-)
I've used includes(x) rather than indexOf(x) !== -1 above. I find it clearer.
You would still need to wait with the action till promise is resolved and finished. So you would check the conditions inside of promise callback and then make adequate actions. Until you have resolved promise, you can display some "loading" information.
Maybe this solution You want?
if (condition1 & condition2) {
something = this.props.something.filter(1)).then(this.props.something.filter(2)
} else if (condition1) {
something = this.props.something.filter(1)
} else if (condition2) {
something = this.props.something.filter(2)
}

Categories

Resources