Best way to check against predefined values - javascript

This is a calendar check. For example: 100 days of history is available but 101 days back would be disabled for certain market.
Code is following:
const todaysDate3 = dayjs().subtract(101, 'days').format('DD')
const todaysDate4 = dayjs().subtract(100, 'days').format('DD') //etc
cy.visit(`http://calendar.whatever/ICN&markettype=ICN`);
cy.get('.calendar-table').click
cy.get('.calendar-table').contains('td',(todaysDate3)).should("have.class","disabled")
cy.get('.calendar-table').contains('td',(todaysDate4)).should("have.class","enabled")
What would be the best practice to make such test for 80, 100, 365 etc days as every market. Worst case scenario I can think of is something like
export const 100days = [{
"url": (`http://calendar.whatever/ICN`),
"has100days": true
}]
and like this for every possible value and using
if (curr.has100days) //do something } else if (curr.has365days){do something else}
Probably best would be to write some kind of function?
thank you for your help!

Since you visit each market, the data-driven approach you indicate is best.
const history = [
{ market: 'ICN', days: 100 },
{ market: 'ZYX', days: 120 },
...
]
history.forEach(data => {
cy.log(`Testing ${data.market} with ${data.days} history`)
cy.visit(`http://calendar.whatever/ICN&markettype=${data.market}`)
const outsideHistory = dayjs().subtract(data.days+1, 'days')
.format('D') // no leading '0'
const insideHistory = dayjs().subtract(data.days, 'days')
.format('D') // no leading '0'
const outsideHistoryRegex = new RegExp(`^${outsideHistory}`) // ^ = startsWith
const insideHistoryRegex = new RegExp(`^${insideHistory}`)
cy.get('.calendar-table').click
cy.get('.calendar-table').contains('td', outsideHistoryRegex)
.last()
.should("have.class","disabled")
cy.get('.calendar-table').contains('td', insideHistoryRegex)
.last()
.should("have.class","enabled")
}
I'm assuming you only want to check the history boundary for each market, but if you want to check multiple dates per market
const history = [
{ market: 'ICN', days: 85 },
{ market: 'ICN', days: 100 },
{ market: 'ICN', days: 365 },
{ market: 'ZYX', days: 120 },
...
]
// Same function...

You can do something like this:
cy.get('.calendar-table')
.find('td')
.then(($ele) => {
if ($ele.text().includes(todaysDate3)) {
cy.wrap($ele).should('have.class', 'disabled')
//Do Something
} else if ($ele.text().includes(todaysDate4)) {
cy.wrap($ele).should('have.class', 'enabled')
//Do Something
} else {
//Do something
}
})

Related

Filter by ID using PokeAPI React

I'm trying to make a Region filter for my Pokedex app (first project using React), and I have an idea of what to do but can't make it work. The PokeAPI doesn't have an endpoint that returns data by Region/Generation, but when you fetch all Pokemon data they are already ordered by generation, so my approach is to replace the limit/offset of the data with the "startId" and "endId" of the Pokemon depending on the chosen Region (eg. if "Johto" is selected, startId = 152 and endId = 251). The thing is I can't figure out how to actually put it in place and make it work together with my Type filter (which fetches the data from a Type endpoint) and keep the pagination working too.
Also, if there's a better approach for this, please let me know! This is what I came up with but doesn't mean it's the best way.
This is the fetch const:
const fetchPokemon = async () => {
try {
setLoading(true);
var data = null;
var promises = null;
var selectType = selectedType.toLowerCase();
if (selectType === "all") {
data = await getPokemon(24, 24 * page);
promises = data.results.map(async (pokemon) => {
return await getPokemonData(pokemon.url);
});
const results = await Promise.all(promises);
setPokemon(results);
setLoading(false);
setTotal(Math.ceil(data.count / 25));
setNotFound(false);
} else {
const { length, data } = await searchPokemonByType(
selectType,
24,
24 * page
);
promises = data.map(async (pokemon) => {
return await getPokemonData(pokemon.pokemon.url);
});
const results = await Promise.all(promises);
setPokemon(results);
setLoading(false);
setTotal(Math.ceil(length / 25));
setNotFound(false);
}
} catch (err) {}
};
I don't know if I should post any other part of the code, please let me know if I should.
Here's the CodeSandbox link with the whole app: https://codesandbox.io/s/wasivispokedex-kyule7
The only thing that comes to my mind how to make it work is: fetch all pokemons at ones.. store it into pokemon state. then create pagination on pokemon state.
you can write your region as object
const regions = {
all: { name: "all", startId: 1, endId: 898 },
kanto: { name: "kanto", startId: 1, endId: 151 },
johto: { name: "johto", startId: 152, endId: 251 },
hoenn: { name: "hoenn", startId: 252, endId: 386 },
sinnoh: { name: "sinnoh", startId: 387, endId: 493 },
unova: { name: "unova", startId: 494, endId: 649 },
kalos: { name: "kalos", startId: 650, endId: 721 },
alola: { name: "alola", startId: 722, endId: 809 },
galar: { name: "galar", startId: 810, endId: 898 }
};
So then you can fetch the start and end id with: regions['johto'].startId (for exmaple)
also add filtered pokemons state - on change region (or any change) you can use filter to filter the result you want to display.
setFilteredPokemons(() => {
pokemon.filter((pokemon) => {
return (
pokemon.id > regions[e.target.value].startId &&
pokemon.id < regions[e.target.value].endId
);
});
});
at the end you want to check if the filtered pokemons length > 0, if so you want to display the filtered one. if not you want to display pokemon state (from the original fetch). you can apply the pagination on both of them.
In my opinion its better in this case to fetch all the data from the server and manipulate it on the front end than calling fetch on every change... (less walks to the server/api and easier life to manipulate the data)
I hope something from here helped.
I wanted to create a working example in codesandbox, but i will need to refector alot of your code. therefore i wanted to share my opinion and suggestions. so you can figure out yourself. maybe with less changes..
If you find it helpful and If you need extra information about this aproach please let me know. i will try my best to help.

Aggregating data after building multi-level array in javascript

I have a reduce function that is building multiple levels and is working perfectly for me except for one issue
Currently, it's building data based on employee first, then by date, area, and job. I'm getting all of the data at the proper level but I'm now trying to aggregate certain data for a totals section at the date level and it's just listing values rather than aggregating.
Basically, in the line I've notated below, I'd like to create a value called total_scans that simply adds up ALL scans for any orders on that date. In other words, for the record for Miranda on 8/12 I would expect the total_scans at the date level to have 49 as the value. Am I on the right track?
const nest = (rows) =>
rows.reduce(
(a, row) => {
const employee = a[row.employee] || (a[row.employee] = { dates: {} })
const date = employee.dates[row.job_date] || (employee.dates[row.job_date] = { areas: {} })
const order = date.areas[row.area_number] || (date.areas[row.area_number] = { jobs: {} })
const job = order.jobs[row.job] || (order.jobs[row.job] = { hours: '', scans: '', job_date: '' })
job.hours += row.hours
job.scans += row.scans
job.job_date = row.job_date
//this line is my issue
date.total_scans += job.scans
return a
},
{}
);
new Vue({
el: "#app",
props: {
},
data: {
rows: [
{
employee: "Miranda",
job: "123",
hours: "10",
job_date: "08/12/2021",
scans: 37,
area_number: "1234567",
},
{
employee: "Miranda",
job: "167",
hours: "15",
scans: 12,
job_date: "08/12/2021",
area_number: "1234568",
},
{
employee: "Miranda",
job: "184",
hours: "18",
scans: 24,
job_date: "08/13/2021",
area_number: "1234569",
}
],
},
computed: {
numbersByEmployee() {
return nest(this.rows)
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
{{numbersByEmployee}}
</div>
Your usage of reduce is a little irregular. The idea of reduce is to take an iterable (array) and return a single value, usually something like a String or Number.
Also, you're causing all sorts of side effects in your reducer, by modifying the object and arrays. Since Javascript is pass-by-reference for arrays and objects, those changes you're causing will be reflected in the original object, which is not how Vue prescribes things are done. If you want to modify data, it should be done in a watch, not a computed.
Finally, I believe you're overcomplicating your reduce function. Instead of a long reduce like that, you could simply do the below. Note the initialValue of 0.
const nest = (rows) =>
rows.reduce(
(sum, row) => {
return sum + row['scans'];
},
0
);
Obviously this will count all scans. If you want to only count scans by date, how about save yourself running the reducer across the array, and instead run filter first? Something like
const nest = (rows) =>
rows.filter(({job_date}) => job_date === SomeDate).reduce(...)
The {job_date} is a destructuring assignment. You could also split out a date filtered array into its own computed.

How to set array state in React Native

I have an array in my state and I want to set array specific values to its objects.
The code looks like this:
state = {
course: "",
type: "",
days: [
{
day: "",
rstime: "",
retime: "",
}
],
}
createSchedule = () => {
const { course,
module,
days
} = this.state;
data.push({
course,
module,
days[]
})
}
For now, I am able to set the state of course and module. I want to know how to set the days so that a course can have multiple days and different times.
The output should look like this for instance:
Course: Biology
Type: Lecture
days:
{
Monday
09:30
11:40
}
{
Friday
15:30
16:40
}
You can something like this,
const days = [...this.state.days];
days.push({
Friday
15:30
16:40
});
this.setState({days});

Determine the average value across a range of data

I have collected an array of weather data that looks like this:
const data = [{
"city_name": "London",
"lat": 51.507351,
"lon": -0.127758,
"main": {
"temp": 289.89,
"temp_min": 288.468,
"temp_max": 291.15,
"feels_like": 287.15,
"pressure": 1004,
"humidity": 77
},
"wind": { "speed": 5.1, "deg": 230 },
"clouds": { "all": 90 },
"weather": [
{
"id": 804,
"main": "Clouds",
"description": "overcast clouds",
"icon": "04n"
}
],
"dt": 1593561600,
"dt_iso": "2020-07-01 00:00:00 +0000 UTC",
"timezone": 3600
},
...
];
This data continues in ascending date order (hour by hour), for the last 40 years.
(sample: https://pastebin.com/ciHJGhnq ) - the entire dataset is over 140MB.
From this data, I'd like to obtain the average temperature (object.main.temp) for each Month and Week of month, across the entire dataset.
The question I am trying to answer with my data is:
What is the average temperature for January, across the last 40 years.
What is the average temperature for February, across the last 40 years.
...
(get the temperature of each week in January and divide by the number of weeks, repeat for all of the other Januaries in the dataset and average that out too).
Repeat for remaining months.
The output I am aiming to create after parsing the data is:
{
[
"JANUARY": {
"weekNumber": {
"avgWeekTemp": 100.00
}
"avgMonthTemp": 69.00,
...
},
...
]
}
The city name & structure of the objects are always the same, in this case London.
// build a unique number of months
// work through our data to work out the week numbers
// work through the data once again and place the data in the right week inside finalOutput
// work through the final output to determine the average values
Unfortunately I'm not very proficient in JavaScript, so I couldn't get past the second obstacle:
"use strict";
const moment = require("moment");
const data = require("./data.json");
let months = [
{
January: [],
},
{
February: [],
},
{
March: [],
},
{
April: [],
},
{
May: [],
},
{
June: [],
},
{ July: [] },
{ August: [] },
{ September: [] },
{ October: [] },
{ November: [] },
{ December: [] },
];
const finalOutput = [];
finalOutput.push(...months);
data.forEach((object) =>
finalOutput.forEach((month) => {
if (
Object.keys(month)[0] === moment(new Date(object.dt_iso)).format("MMMM")
) {
[month].push(object.dt_iso);
}
})
);
console.log(finalOutput);
Which only returned the array of months with nothing in each month.
[
{ January: [] },
{ February: [] },
{ March: [] },
{ April: [] },
{ May: [] },
{ June: [] },
{ July: [] },
{ August: [] },
{ September: [] },
{ October: [] },
{ November: [] },
{ December: [] }
]
How can I calculate the average values per week & month across my entire data set?
I'm going to write your script for you, but while you wait here's some high-level guidance.
First, let's study your data. Each row is an hourly weather measurement. As a result, each datapoint you want will be an aggregate over a set of these rows. We should organize the script along those lines:
We'll write a function that accepts a bunch of rows and returns the arithmetic mean of the temperatures of those rows: function getAvgTemp(rows) => Number
We'll write another function that takes a bunch of rows, plus the desired month, and returns all the rows for just that month: function getRowsByMonth(month) => Array(rows)
We'll write another function that takes a bunch of rows, plus the desired week number, and returns all the rows for just that week: function getRowsByWeekNumber(rows, weekNumber) => Array(rows)
^^ that's if "week number" means 1-52. But if "week number" means "week within the month," then instead we'll do:
A function will also take a month: function getRowsByMonthWeek(rows, month, weekNumber) => Array(rows)
From these basic building blocks, we can write a routine that assembles the data you want.
What would that routine look like?
Probably something like this:
Loop through all the months of the year. We won't look in the data for these months, we'll hard-code them.
For each month, we'll call getRowsByMonth on the full data set. Call this set monthRows.
We'll pass monthRows to getAvgTemp -- it doesn't care what the timespan is, it just extracts and crunches the temp data it receives. That's our avgMonthTemp solved for.
Depending on what you mean by "week number," we'll divide the monthRows into smaller sets and then pass each set into getAvgTemp. (The hardest part of your script will be this division logic, but that's not to say it will be that hard.) This gives us your weekly averages.
We'll assemble these values into a data structure and insert it into the final structure that ultimately gets returned/logged.
Here's the implementation. It's a little different than I expected.
The biggest change is that I did some pre-processing up front so that the date values don't have to be parsed multiple times. While doing that, I also calculate each row's weekNumber. As a consequence, the week logic took the form of grouping rows by their weekNumbers rather than querying the dataset by weekNumber.
Some notes:
I decided that "weekNumber" means "week-of-year."
Instead of using Moment, I found a week-number algorithm on StackOverflow. If you want to use Moment's algo instead, go ahead.
The output data structure is not what you described.
Your example is not valid JSON, so I made a guess as to what you had in mind.
Here's an example of what it looks like:
{
"JUNE": {
"avgMonthTemp": 289.9727083333334,
"avgWeekTemps": {
"25": 289.99106382978727,
"26": 289.11
}
},
"JULY": {
"avgMonthTemp": 289.9727083333334,
"avgWeekTemps": {
"27": 289.99106382978727,
"30": 289.11
}
}
}
The output will include a top-level entry for every month, whether or not there is any data for that month. However, the avgWeekTemps hash will only have entries for weeks that are present in the data. Both behaviors can be changed, of course.
It's a reusable script that processes arbitrary JSON files in the format you shared.
You mentioned that each file has data from one city, so I figured you'll be running this on multiple files. I set it up so you can pass the path to the data file as a command-line argument. Note that the CLI logic is not sophisticated, so if you're doing funky things you will have a bad time. Doing CLI stuff well is a whole separate topic.
If your data for London is in a file named london.json, this is how you would process that file and save the results to the file london-temps.json:
$ node meantemp.js london.json > london-temps.json
// meantemp.js
const FS = require('fs')
// sets the language used for month names
// for language choices, see: http://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
const MONTH_NAME_LANG_CODE = 'en-US'
// generate the list of month names once
const MONTH_NAMES = Array(12).fill().map(
( _, monthNum ) => new Date(2020, monthNum).toLocaleDateString(MONTH_NAME_LANG_CODE, { month: 'long' }).toUpperCase()
)
main()
function main() {
let filepath = process.argv[2]
let cityData = readJsonFile(filepath)
// before working on the data, prep the date values for processing
let allRows = cityData.map(row => {
let _date = new Date(row.dt_iso)
let _weekNum = getWeekNum(_date)
return { ...row, _date, _weekNum }
})
let output = MONTH_NAMES.reduce(( hash, monthName, monthNum ) => {
// grab this month's rows
let monthRows = getRowsForMonth(allRows, monthNum)
// calculate monthly average
let avgMonthTemp = getMeanTemp(monthRows)
// calculate weekly averages
let rowsByWeekNum = groupRowsByWeekNum(monthRows)
let avgWeekTemps = Object.keys(rowsByWeekNum)
.reduce(( hash, weekNum ) => ({
...hash,
[weekNum]: getMeanTemp(rowsByWeekNum[weekNum])
}), {})
return {
...hash,
[monthName]: { avgMonthTemp, avgWeekTemps }
}
}, {})
console.log(JSON.stringify(output))
}
function readJsonFile( path ) {
try {
let text = FS.readFileSync(path, 'utf8')
return JSON.parse(text)
} catch ( error ) {
if(error.code === 'ENOENT') {
console.error(`Could not find or read path ${JSON.stringify(path)}`)
process.exit()
} else if(error instanceof SyntaxError) {
console.error(`File is not valid JSON`)
process.exit()
} else {
throw error
}
}
}
function getRowsForMonth( rows, monthNum ) {
return rows.filter(row => monthNum === row._date.getUTCMonth())
}
function groupRowsByWeekNum( rows ) {
return rows.reduce(( hash, row ) => {
if(!hash.hasOwnProperty(row._weekNum)) {
hash[row._weekNum] = []
}
hash[row._weekNum].push(row)
return hash
}, {})
}
// ISO8601-compliant week-of-year function
// taken from https://stackoverflow.com/a/39502645/814463
// modified by me to prevent mutation of args
function getWeekNum( date ) {
// if date is a valid date, create a copy of it to prevent mutation
date = date instanceof Date
? new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate())
: new Date()
let nDay = (date.getDay() + 6) % 7
date.setDate(date.getDate() - nDay + 3)
let n1stThursday = date.valueOf()
date.setMonth(0, 1)
if (date.getDay() !== 4) {
date.setMonth(0, 1 + ((4 - date.getDay()) + 7) % 7)
}
return 1 + Math.ceil((n1stThursday - date) / 604800000)
}
function getMeanTemp( hourlyReadings ) {
let temps = hourlyReadings.map(reading => reading.main.temp)
let mean = getMean(temps)
return mean
}
function getMean( numbers ) {
let sum = numbers.reduce(( sum, num ) => sum + num, 0)
let mean = sum / numbers.length
return mean
}

variable cannot be reached inside foreach loop

I am trying to build a webhook for dialogue flow but cannot access some variable in code
when i run code
match comes out to be undefined
var findclass = (data, day, time) => {
var match;
data.forEach(entry => {
if (entry.day === day && entry.time === time) {
console.log("found");
match=entry;//this statement has no effect on above var match
//instead it creates a new local variable
}
});
return match;
}
exports.tt = functions.https.onRequest((request, response) => {
let qr = request.body.queryResult;
let day = qr.parameters.day;
let time = parseInt(qr.parameters.time.substring(11, 13));
let data = [
{
day: "monday",
time: 11,
type: "lecture",
batches: ["A", "B1", "B4", "B3", "B2"],
subject: {
name: "Basic Numerical Methods",
name_short: "BNM",
coursecode: "17B1NMA531",
coursecode_short: "17MA531"
},
class: "CS1",
teachers: "SSH",
semester: 5
},
{
day: "monday",
time: 15,
type: "lecture",
batches: ["A6", "A9"],
subject: {
name: "Environmental Science",
name_short: "EVS",
coursecode: "15B11GE301",
coursecode_short: "GE301"
},
class: "CS1",
teachers: "EKT",
semester: 5
}]
var match = findclass(data, day, time);
console.log(JSON.stringify(match));
if (match) {
response.send({
fulfillmentText: `Yes, ${match.subject.name_short || match.subject.name}`
});
} else {
response.send({
fulfillmentText: `No class on ${day} ,${time} hr`
});
}
also vscode code is showing var match is unused if i remove return match statement,this means it is not considering match = entry , but i cannot understand why ?
I don't know the reason but two changes fixed the code
1) Adding "use strict" at the top.
2) defining function as
const findclass = () => {}
instead of var
My guess is that your declaration of:
var match ...
is being hoisted ... see:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var
In your code you have declared:
var match ...
twice. I think the declaration in the body of the findClass function might want to be changed to a local variable ...
let match;
(This is a guess, will delete this answer on request or if better answer comes along).
My guess is that nothing is actually matching in the loop, and therefore match remains undefined. Have you made sure something is actually matching in the loop?
Also, in VSCode, if you are just assigning a variable, but not using it (e.g., in a return statement), it will show the variable as being unused, so that is expected.

Categories

Resources