How can I make Javascript execute top to bottom - javascript

I have been trying to get this code to execute in order but for some reason the asssignments action.payload.subject.position.year = action.payload.year; action.payload.subject.position.quarter = action.payload.quarter; execute before even the console.log at the top of the function. This is messing things on alot of levels across my code. I have always understood JS as being interpreted and top to bottom but this is giving me alot of headaches.
case ACTIONS.ADD_SUBJECT_TO_QUARTER:
{
const yearProp = `year${action.payload.year}`;
const quarterProp = `quarter${action.payload.quarter}`;
console.log(
"ADD_SUBJECT_TO_QUARTER",
curricullum[yearProp][quarterProp].subjects,
action.payload.subject
);
if (!curricullum[yearProp][quarterProp].subjects.some(
(subject: any) => subject.id === action.payload.subject.id
)) {
if (
curricullum[yearProp][quarterProp].totalCredits
+ action.payload.subject.credit > 15
) {
alert("You can't add more than 15 credits in a quarter");
return curricullum;
}
action.payload.subject.position.year = action.payload.year;
action.payload.subject.position.quarter = action.payload.quarter;
console.log("position", action.payload.subject.position);
return {
...curricullum,
[yearProp]: {
...curricullum[yearProp],
[quarterProp]: {
...curricullum[yearProp][quarterProp],
subjects: [
...curricullum[yearProp][quarterProp].subjects,
action.payload.subject,
],
totalCredits: curricullum[yearProp][quarterProp].totalCredits +
action.payload.subject.credit,
},
},
subjectList: {
subjects: curricullum.subjectList.subjects.filter(
(subject: any) => subject.id !== action.payload.subject.id
),
},
};
} else {
return curricullum;
}
}
This is the colnsole.log that i get in the browser; the important part is that position year and quarter should be 0 at that point as the console.log happens before the assignment
{id: 1, language: 'English', credit: 15, courseName: 'OOP', version: '5', …}
courseName
:
"OOP"
credit
:
15
description
:
"Object Oriented Programming"
id
:
1
language
:
"English"
learningGoalIds
:
undefined
position
:
{year: 1, quarter: 1}
version
:
"5"
[[Prototype]]
:
Object
Update: Okay figured out that using JSON.stringify would solve the console.log problem, but the main problem remains with this
case ACTIONS.ADD_SUBJECT_TO_SUBJECTLIST:
console.log("ADD_SUBJECT_TO_SUBJECTLIST", action.payload.subject);
const yearProp = `year${action.payload.subject.position.year}`;
const quarterProp = `quarter${action.payload.subject.position.quarter}`;
action.payload.subject.position.year = 0;
action.payload.subject.position.quarter = 0;
return {
...curricullum,
subjectList: {
subjects: [
...curricullum.subjectList.subjects,
action.payload.subject,
],
},
[yearProp]: {
...curricullum[yearProp],
[quarterProp]: {
...curricullum[yearProp][quarterProp],
subjects: curricullum[yearProp][quarterProp].subjects.filter(
(subject: any) => subject.id !== action.payload.subject.id
),
},
},
};```
yearprop and quarterprop are updated after the assignments in the lines below them and that makes it impossible to access the values. Basically I need to make year and quarter equal to 0 while at the same time keeping a record of what they were befroe to use it in the return function.

Related

How do I change the value of the object's keys depending on the condition in ES6/React

How do I Change the value of the object's keys depending on the condition.
What is the best way to structure and code this?
Data
const newData = {
id: 111,
name: "ewfwef",
description: "Hello",
step: 0
};
Code
.then((res)) => {
switch(res.data.status) {
case 'data_added': {
step = 1;
break;
}
case 'product_added': {
step = 2;
break;
}
case 'consumer_added': {
step = 3;
break;
}
case 'water_added': {
step = 4;
break;
}
case 'things_added': {
step = 5;
break;
}
case 'funky_added': {
step = 6;
break;
}
default: {
step = 1;
break;
}
}
const data = Object.keys(newData).map((key) => ({ key, value: newData[key] }));
}
Conditions
step value should be replace based on this condition:
'data_added' = 1
'product_added' = 2
'consumer_added' = 3
'water_added' = 4
'things_added' = 5
'funky_added' = 6
Expected Output
[
{
key: id,
value: 111
},
{
key: name,
value: 'ewfef'
},
{
key: description,
value: 'Hello'
},
{
key: step,
value: 2
}
]
Seems to me like your trying to manage some complicated state. Since you are using React, why not use a reducer?
https://reactjs.org/docs/hooks-reference.html#usereducer
Depending on your use case, you might also find the context module pattern interesting. Coupled with a well-written reducer and actions, it could make your stateful logic reusable and at the same time provide a natural place for you to do these updates to your state using actions and the flux pattern.
You can check out an example of the context module pattern in action here:
https://github.com/ashkan-pm/react-context-module-pattern
I also suggest you check out some blog posts by Kent C. Dodds on this:
https://kentcdodds.com/blog/the-state-reducer-pattern-with-react-hooks
https://kentcdodds.com/blog/how-to-use-react-context-effectively
May be you can use an object for mapping the status to step:
var stepMap = {
"data_added": 1,
"product_added": 2,
"consumer_added": 3,
"water_added": 4,
"things_added": 5,
"funky_added": 6
}
And then, use it this way,
.then((res)) => {
const status = res.data.status;
if (stepMap[status]) { //checks if the response status is part of our map
newData.step = stepMap[status];
}
const data = Object.keys(newData).map((key) => ({ key, value: newData[key] }));
console.log(data);
}):
If I have well understood : you want to update newData.step to set it to your res.data.status ?
Since your cases are well known, there is how I would do it :
const STEPS = {
'data_added' : 1,
'product_added' : 2,
'consumer_added' : 3,
'water_added' : 4,
'things_added' : 5,
'funky_added' : 6
};
const newData = {
id: 111,
name: "ewfwef",
description: "Hello",
step: 0
};
/*...*/.then((res)) => {
newData.step = STEPS[data.res.status];
const data = Object.keys(newData).map((key) => ({ key, value: newData[key] }));
});
What i don't understand is this line : const data = Object.keys(newData).map((key) => ({ key, value: newData[key] }));
What you do there is listing newData keys, then returning an array of key/value pairs of each of it's properties. Is that part of your problem ?
In that cas, you may want to replace the step attribute only in this key/value pair array for the step attribute. So, then you should do something like this :
/*...*/.then((res)) => {
const data = Object.keys(newData).map((key) => {
if(key === 'step') return { key, value : STEPS[data.res.status] };
else return { key, value: newData[key] };
);
});

bug from element showing

Hi all I have following code: my code
In code I am recieving data from backend, In my case I hardcoded that part (see below data)
const skills = [
{
id: 1,
name: "Html "
},
{ id: 2, name: "CSS" },
{ id: 3, name: "Scss" },
{ id: 4, name: "Bootstrap 4" },
{ id: 5, name: "JavaScript" },
{ id: 6, name: "Jquery" },
{ id: 7, name: "React JS" },
{ id: 8, name: "Angular " },
{ id: 9, name: "Vue.js " },
{ id: 10, name: "SQL" },
{ id: 11, name: "php" },
{ id: 12, name: "Laravel" }
];
I am counting all my skill names char length and if that length is greater then allowCharCount I am hiding all rest skills and showing in number how many skills is hided. That part is working.
let lengthCount = 0;
let maxIndex = 0;
const allowCharCount = 20;
const skill = [];
export const Skill = ({ data }) => {
if (data === undefined) {
return null;
} else {
data.map((item) => {
if (lengthCount <= allowCharCount) {
maxIndex = item.id;
skill.push(item);
}
lengthCount += item.name.length;
});
const mySkills = skill.map((perSkill) => (
<span key={perSkill.id} className="skillItem">
{perSkill.name}
</span>
));
return (
<div className="skillWrapper">
<div>{mySkills}</div>
{lengthCount > allowCharCount ? (
<div className="skillNumber">+{data.length - maxIndex}</div>
) : null}
</div>
);
}
};
but when my chars count is less then allowCharCount it's not working.
If I only have first 3 items (Html, CSS, Scss) I see following view
Html CSS Scss Html CSS Scss +0.
Please help me to fix this code for that case (if chars count is less than allowCharCount) I need to show only correct items with no any +0 count
You should rethink the way you write the component and code in general
You have to store dynamic variables inside the component
To save the result of array operations, you should either mutate state or create a new variable inside the component (which is more preferable in your case).
.map method return an array of values returned by callback inside of it. If you're not going to return anything, use .forEach instead
Use .length property instead of incrementing the size of array by yourself to avoid bugs.
The reason why you get duplicated elements is that you don't clear the array before the component updates, so the algorithm just pushes these values again.
Working code example:
export const Skill = ({ data }) => {
// here I invert condition to get the rest code out of brackets
if (!data) return null;
// all of the dynamic data should be inside the component
const failIndex = React.useMemo(() => {
let charCount = 0;
for (let i = 0; i < data.length; i++) {
charCount += data[i].name.length;
if (charCount > allowCharCount) return i;
}
return -1;
}, [data]);
// check if data has element that doesn't pass the condition
const dataBeforeFail = failIndex === -1 ? data : data.slice(0, failIndex + 1);
const hiddenSkillsCount = data.length - dataBeforeFail.length;
return (
<div className="skillWrapper">
<div>
{dataBeforeFail.map((perSkill) => (
<span key={perSkill.id} className="skillItem">
{perSkill.name}
</span>
))}
</div>
{hiddenSkillsCount > 0 && (
<div className="skillNumber">+{hiddenSkillsCount}</div>
)}
</div>
);
};

JavaScript - Properly Extract Deep Object Properties and Construct New Object

Suppose the following array of objects is returned from an API:
const data = [
{ // first item
meta: {
stems: [
"serpentine",
"serpentinely"
]
},
hwi: {
hw: "sep*pen*tine",
prs: [
{
mw: "ˈsər-pən-ˌtēn",
sound: {
audio: "serpen02"
}
},
]
},
shortdef: [
"of or resembling a serpent (as in form or movement)",
"subtly wily or tempting",
"winding or turning one way and another"
]
},
{ // second item
meta: {
stems: [
"moribund",
"moribundities",
"moribundity"
]
},
hwi: {
hw: "mor*i*bund",
},
fl: "adjective"
}
]
I want to create a function that will generate a new array of objects. The objects in this new array will consist of data from the old objects, just rearranged. This is how I expect a new array to look, for example:
[
{
word: 'serpentine',
definitions: [
'of or resembling a serpent (as in form or movement)',
'subtly wily or tempting',
'winding or turning one way and another'
]
},
{
word: 'moribund',
definitions: [
'being in the state of dying : approaching death',
'being in a state of inactivity or obsolescence'
],
partOfSpeech: 'adjective'
}
]
I do this with the following function:
const buildNewData = arr => {
const newData = []
arr.forEach(item => {
newData.push({
...item.meta.stems[0] && { word: item.meta.stems[0]},
...item.shortdef && { definitions: item.shortdef },
...item.fl && { partOfSpeech: item.fl },
...item.hwi.prs[0].mw && { pronunciation: item.hwi.prs[0].mw}
})
})
return newData
}
buildNewData(data)
You may be curious as to why I use ...item.meta.stems[0] && { word: item.meta.stems[0]} in the creation of the new objects. This is to check if the property exists in the original object. If it doesn't exist, the expression will evaluate to false and therefore not be added to the new object. The first object in the original array does not have the fl property, so it evaluates to false when the new object is being constructed.
But this doesn't work when looking up a property that is an array. The code above fails with the error: TypeError: Cannot read property '0' of undefined. That's because the second item does not have a prs array under the hwi property, so the lookup fails.
Since I cannot control what data is returned from the API, how do I write a function that successfully creates a new array of objects in the format I've specified, without causing an error? I already have a solution to not add particular properties if they do not exist, but how do I take into account arrays?
More generally, I'm curious if there is a standardized way of extracting data from objects programmatically that prevents errors like this from occurring. Is there a better way to do this?
You need an additional guard so:
...item.hwi.prs[0].mw && { pronunciation: item.hwi.prs[0].mw}
becomes
...(Array.isArray(item.hwi.prs) && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
which can be shortened to:
...(item.hwi.prs && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
if you are confident that if item.hwi.prs exists its value will be an array that has a 0 value that can be spread.
const data = [
{ // first item
meta: {
stems: [
"serpentine",
"serpentinely"
]
},
hwi: {
hw: "sep*pen*tine",
prs: [
{
mw: "ˈsər-pən-ˌtēn",
sound: {
audio: "serpen02"
}
},
]
},
shortdef: [
"of or resembling a serpent (as in form or movement)",
"subtly wily or tempting",
"winding or turning one way and another"
]
},
{ // second item
meta: {
stems: [
"moribund",
"moribundities",
"moribundity"
]
},
hwi: {
hw: "mor*i*bund",
},
fl: "adjective"
}
];
const buildNewData = arr => {
const newData = []
arr.forEach(item => {
newData.push({
...item.meta.stems[0] && { word: item.meta.stems[0]},
...item.shortdef && { definitions: item.shortdef },
...item.fl && { partOfSpeech: item.fl },
...(Array.isArray(item.hwi.prs) && item.hwi.prs[0].mw) && { pronunciation: item.hwi.prs[0].mw}
})
})
return newData
}
let newData = buildNewData(data);
console.log(newData);
As you need to check existence of properties in an Object:
Use Optionnal chaining: https://javascript.info/optional-chaining
It returns a type undefined if the prop doesn't exist (but not string "undefined" ;) )
For desired order in new array, add numbers before the names of props.
let newData = [];
for (let i = 0; i < data.length; i++) {
newData[i] = {};
if (data[i]?.meta?.stems[i] != undefined)
newData[i].word = data[i].meta.stems[i];
if (data[i]?.shortdef != undefined) {
newData[i].definitions = data[i].shortdef.join(', ') + '.';
newData[i].definitions = newData[i].definitions.charAt(0).toUpperCase() + newData[i].definitions.substring(1); // Capitalize first letter
}
if (data[i]?.fl != undefined)
newData[i].partOfSpeech = data[i].fl;
}
console.log(...newData);

Check to see that every field in an array equals a value

I've got data like this:
this.registrations = [{
event: "Juniors 18s",
day: "Friday",
group: "nonpro",
players: [{
first: "Mary",
last: "Mack",
email: "marymack#dressedinblack.com",
phone: "8888675309",
signed: false,
waivers: [{
has_signed: true,
token: "ab",
url: "somesite.com",
signatureUrl: "someothersite.com",
message: "Minor waiver"
},
{
has_signed: true,
token: "ab",
url: "somesite.com",
signatureUrl: "someothersite.com",
message: "Parental waiver"
}
]
},
{... another record like that one}
]
Is there a way to display if each player has signed all the waivers? I know there is a .every function but I'm not sure how to use it with this nested data structure.
I was thinking something like below, but it didn't work:
this.registrations.has_signed = this.registrations.waivers.every( waiver => waiver.has_signed === true )
Then I tried this one, which at least seems like it might be closer to a win (but not quite). the players.signed property isn't used, but someone put it in there as false by default, so I'd love to use it:
this.registrations.forEach(reg => {
reg.players.forEach(p => {
if (p.waivers.every(waiver => waiver.has_signed === true)) {
p.signed = true;
} else {
p.signed = false;
}
return reg;
});
});
This snippet should do the required task.
const modifiedPlayers = this.registrations.map(reg => reg.players).map(player => {
player.signed = player.waivers.every(waiver => waiver.has_signed);
return player;
});
this.registrations.players = modifiedPlayers;
this.registrations.has_signed = false;
this.registrations.forEach(registration =>
registration.players.forEach(player =>
this.registrations.has_signed = player.waivers.every(waiver => waiver.has_signed)
)
);
You have to loop the outer arrays first to get to the waivers and use every to find out the has-signed.

Checking if Ticker is in another list, then adding a new key [RamdaJS]

I'm trying to use Ramda to compare 2 lists, find which tickers in the tickersList are also in the portfolioTickers. If one is in the portfolio list as well then add a key portfolio:true.
Example data:
tickers = [
{ ticker: aa },
{ ticker: bb },
{ ticker: cc }
]
portfolio = [
{ ticker: aa }
]
Expected result:
tickers = [
{ ticker: aa, portfolio: true },
{ ticker: bb },
{ ticker: cc }
]
Sounds simple, but stuck here so far:
const checkTicker = _.curry((ticker, portTicker) => {
if (ticker.ticker === portTicker.ticker) {
ticker.portfolio = true;
}
return ticker;
});
const matched = R.anyPass([checkTicker]);
const setPortfolioFlag = _.curry((portfolioTickers, ticker) => {
// const matched = R.filter(checkTicker(ticker), portfolioTickers);
// console.log('matched', matched)
const each = matched(R.forEach(checkTicker(ticker), portfolioTickers));
console.log('each', each)
return each;
});
const inPortfolioCheck = (tickersList) => {
const portfolioTickers = R.filter(TickersFactory.isTickerInPortfolio, tickersList);
const tickers = R.map(setPortfolioFlag(portfolioTickers), tickersList);
console.log('tickers', tickers)
};
const displayTickers = (tickers) => {
this.tickersList = tickers;
const checkedTickers = inPortfolioCheck(this.tickersList);
console.log('checkedTickers', checkedTickers)
};
Right now each is always true, and the list that all this logic returns is just a list the same length as my tickerList, but just true'.
A problem I keep running into is I feel that I need to run another 2nd R.map to do the check, however the result of that returned map is the Portfolio tickers, and not the original list.
Working code, but with an ugly for loop:
This obviously works because I'm using a for loop, but I'm trying to remove all object oriented code and replace with functional code.
const setPortfolioFlag = _.curry((portfolioTickers, ticker) => {
for (var i=0; i<portfolioTickers.length; i++) {
if (portfolioTickers[i].ticker === ticker.ticker) {
ticker.portfolio = true;
}
}
return ticker;
});
const inPortfolioCheck = (tickersList) => {
const portfolioTickers = R.filter(TickersFactory.isTickerInPortfolio, tickersList);
return R.map(setPortfolioFlag(portfolioTickers), tickersList);
};
const displayTickers = (tickers) => {
this.tickersList = inPortfolioCheck(tickers);
console.log('this.tickersList', this.tickersList)
};
LoDash version of the forLoop:
_.each(portfolio, (port) => {
if (port.ticker === ticker.ticker) {
ticker.portfolio = true;
}
});
So the first thing you might notice is that the expected resulting tickers array is the same "shape" as the input tickers array, suggesting that we should be able to make use of R.map for the expression. Knowing that, we can then focus on what just has to happen to the individual elements of the array.
So for each ticker object, when found in the portfolio array, we would like to attach the portfolio: true property.
const updateTicker =
R.when(R.flip(R.contains)(portfolio), R.assoc('portfolio', true))
updateTicker({ ticker: 'aa' })
//=> {"portfolio": true, "ticker": "aa"}
updateTicker({ ticker: 'bb' })
//=> {"ticker": "bb"}
Then we can just map over the tickers list to update each ticker.
R.map(updateTicker, tickers)
//=> [{"portfolio": true, "ticker": "aa"}, {"ticker": "bb"}, {"ticker": "cc"}]
The answer from #scott-christopher is, as always, excellent. But subsequent comments make me think that it doesn't quite cover your use case. If the data is more complex, and it's possible that items in the portfolio and the tickers might have distinct properties, then contains won't be strong enough. So if your data looks more like this:
const tickers = [
{ ticker: 'aa', size: 1 },
{ ticker: 'bb', size: 2 },
{ ticker: 'cc', size: 3 },
{ ticker: 'dd', size: 4 }
]
const portfolio = [
{ ticker: 'aa', id: 'abc' },
{ ticker: 'dd', id: 'xyz' }
]
Then this version might work better:
const updateTicker =
when(pipe(eqProps('ticker'), any(__, portfolio)), assoc('portfolio', true))
This nearly points-free version might be a little obscure. It's equivalent to this one:
const updateTicker =
when(
(ticker) => any(eqProps('ticker', ticker), portfolio),
assoc('portfolio', true)
)
Also, if the any(__, portfolio) notation is unclear, it's equivalent to flip(any)(portfolio).
You can see this on the Ramda REPL.

Categories

Resources