I am trying to create timeline map using echarts js. I hope I am not able to figure out how to create 'optiosn' as required. Using these code, I obtained 'option' as picture below.
import axios from 'axios'
const data = []
const date= []
let options= []
axios.get('https://data.nepalcorona.info/api/v1/covid').then((res) => {
const array = res.data
const groups = array.reduce((groups, info) =>{
const date = info.reportedOn;
if (!groups[date]) {
groups[date] = [];
}
groups[date].push(info);
return groups;
}, {});
const groupingDate = Object.keys(groups).map((date) =>{
return{
date: date,
infos: groups[date]
};
})
const sortedData = groupingDate.sort((a,b) => {
var dateA = new Date(a.date) , dateB = new Date(b.date)
return dateA - dateB
})
for(let i=0; i< sortedData.length; i++) {
date.push(sortedData[i].date)
const array = sortedData[i].infos
data.push(array)
}
const points= [];
const newlist = data.map(x => {
points.push([x[0].point.coordinates[0], x[0].point.coordinates[1], x[0].age]);
return {
series:{
data: points.slice(0)
}
}
})
options.push(newlist)
console.log(options)
})
export default {
...
options: [options][0],
...
}
The result of this is obtained as below:
But i dont want an unnecessary array as it is obtained. I want options as below:
options: Object
---baseOption: Object
---options: Array[67]
I hope you can understand what i am saying.
use the spread operator in your third last line as following
options.push(...newlist)
instead of
options.push(newlist)
Related
I am trying to extract id from the below array of objects and so far I am able to give it a go with the below code but it is showing undefined and cannot get id , would you please check the code and adjust to get id out?
const array = [{
contact: {
id: 1,
email: 'roar#gmail.com',
},
date: '2/4/22'
},
{
contact: {
id: 2,
email: 'grr#gmail.com',
},
date: '2/4/22'
}
]
function extractValue(arr, prop) {
let extractedValue = [];
for (let i = 0; i < arr.length; ++i) {
// extract value from property
extractedValue.push(arr[i][prop]);
}
return extractedValue;
}
const result = extractValue(array, 'contact.id');
console.log(result);
A good way to do this is the Array Map method
This will get all the id's from your array
const result = array.map((val) => val.contact.id)
const extractValue = (array, path) => {
const [first, second] = path.split('.')
if (second) return array.map(obj => obj[first][second])
return array.map(obj => obj[first])
}
const res = extractValue(array, 'contact.id')
console.log(res)
// => [ 1, 2 ]
this will support single and double level nested results
function find(val, arr) {
for (let x of arr)
val = val[x];
return val;
}
function extractValue(arr, prop) {
return array.map(x => find(x, prop.split(".")))
}
In the line setVotedPosts([...previousVotedPosts, postId]);
I'm trying to get the previous value of votedPosts, but I'm getting back the newest value.
full code : https://github.com/silvertechguy/reddit-clone/blob/main/src/components/vote-buttons.js
App live : https://reddit-clone-official.vercel.app/
const VoteButtons = ({ post }) => {
const [isVoting, setVoting] = useState(false);
const [votedPosts, setVotedPosts] = useState([]);
useEffect(() => {
const votesFromLocalStorage =
JSON.parse(localStorage.getItem("votes")) || [];
setVotedPosts(votesFromLocalStorage);
}, []);
const handleDisablingOfVoting = (postId) => {
const previousVotedPosts = votedPosts;
setVotedPosts([...previousVotedPosts, postId]);
localStorage.setItem(
"votes",
JSON.stringify([...previousVotedPosts, postId])
);
};
const handleClick = async (type) => {
setVoting(true);
// Do calculation to save the vote.
let upVotesCount = post.upVotesCount;
let downVotesCount = post.downVotesCount;
const date = new Date();
if (type === "upvote") {
upVotesCount = upVotesCount + 1;
} else {
downVotesCount = downVotesCount + 1;
}
await db.collection("posts").doc(post.id).set({
title: post.title,
upVotesCount,
downVotesCount,
createdAt: post.createdAt,
updatedAt: date.toUTCString(),
});
// Disable the voting button once the voting is successful.
handleDisablingOfVoting(post.id);
setVoting(false);
};
const checkIfPostIsAlreadyVoted = () => votedPosts.includes(post.id);
Problem
const previousVotedPosts = votedPosts;
In JavaScript, arrays are reference types, so you can't just create a new copy of an array using =.
Try this solution
Clone array using spread syntax(...).
const handleDisablingOfVoting = (postId) => {
const previousVotedPosts = [...votedPosts];
setVotedPosts([...previousVotedPosts, postId]);
localStorage.setItem(
"votes",
JSON.stringify([...previousVotedPosts, postId])
);
};
I'm running a Parse Query on my User class.
I want to retrieve users that are contained in an array of strings (ID).
const usersQuery = new Parse.Query(User).containedIn('objectId', customersArrayId).find({ useMasterKey: true });
Which works, actually. I'm getting a [ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }], because only 1 user matches.
But well, my issue is that I'm only getting ParseUser { _objCount: 6, className: '_User', id: 'iYIJ7Zrmms' }. This class contains other fields (firstname, lastname, e.g.) that are not returned.
When I performed the same thing, looping on my customersArrayId and performing .get():
const customerId = customersArrayId[index];
promises.push(new Parse.Query(User).get(customerId).then((user) => {
return user.toJSON();
}, (error) => console.error(error)));
I'm getting the full object, as expected. But it doesn't seem to be the right way of querying Parse objects from an array of ids.
I can't find anything in the docs about it, any idea why containedIn only returns a part of the queried objects?
I actually get the difference:
new Parse.Query(User).get(customerId)
=> returns the Parse Object
new Parse.Query(User).containedIn('objectId', customersArrayId)
=> returns the Parse User, subclass of a Parse Object.
And well, then, this thread was useful: Get user from parse in Javascript
I ended up using :
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
const customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
...
Still not sure that the best answer.
EDIT
router.get('/:userId/shop/customers/details', checkUserMatch, (req, res, next) => {
if('shops' in req.jwtData.data) {
const shopId = req.jwtData.data.shops[0];
const query = new Parse.Query('OtherStuff');
const Shop = Parse.Object.extend('Shops');
query.equalTo('shop', new Shop({id: shopId})).find().then((otherStuffs) => {
const customersArrayId = otherStuffs.map(otherStuff => otherStuff.toJSON().user.objectId);
const usersQuery = new Parse.Query('_User').containedIn('objectId', customersArrayId).find({ useMasterKey: true });
usersQuery.then(customersResponse => {
const customers = [];
for (let index = 0; index < customersResponse.length; index++) {
let customer = {
...customersResponse[index].toJSON(),
...customersResponse[index].attributes
};
const customerId = customer.objectId;
const stuffQuery = new Parse.Query('Stuff').equalTo('user', new UserModel({objectId: customerId})).find().then((stuff) => {
return stuff;
});
const otherStuffQuery = new Parse.Query('otherStuff').equalTo('user', new UserModel({objectId: customerId})).find().then((otherStuff) => {
return otherStuff;
});
Promise.all([stuffQuery, otherStuffQuery]).then((data) => {
const stuff = data[0];
const otherStuff = data[1];
customer = {
...customer,
stuff,
otherStuff,
}
customers.push(customer);
if(index === customersResponse.length - 1) { // last customer
res.json({
success: true,
data: customers
});
}
})
}
});
});
}
});
Here I validate if my users status is true, and if they are, I put them in an array. The thing here is that next time it will validate, all those who already was true will be added to the same array. Can it be solved by filter instead of push, or should I take the validation in any other way?
import {
UPDATE_LIST_SUCCESS
} from './types'
var arr = []
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
snapshot.forEach(function (child) {
var data = child.val()
if (child.val().profile.status === true) {
arr.push(data)
}
})
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
You can do it like this:
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
var arr = snapshot.filter(function (child) {
return child.val().profile.status === true
}).map(function (child) {
return child.val();
});
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
So here is my not so pretty way of solving it, but it works.
import {firebaseRef} from '../firebase/firebase'
import {
UPDATE_LIST_SUCCESS
} from './types'
export const fetchList = () => {
return (dispatch) => {
const arrayToFilter = []
firebaseRef.database().ref().child('users')
.on('value', snapshot => {
let snap = snapshot.val()
// Get acces to the keys in the object i got from firebase
let keys = Object.keys(snap)
// iterate the keys and put them in an User object
for (var i = 0; i < keys.length; i++) {
let k = keys[i]
let name = snap[k].profile.name
let age = snap[k].profile.age
let status = snap[k].profile.status
let profile_picture = snap[k].profile.profile_picture
let users = {name: '', age: '', status: Boolean, profile_picture: ''}
users.name = name
users.age = age
users.status = status
users.profile_picture = profile_picture
// adding the user object to an array
arrayToFilter.push(users)
}
// filter and creates a new array with users depending if their status is true
let arr = arrayToFilter.filter(child => child.status === true)
dispatch({ type: UPDATE_LIST_SUCCESS, payload: arr })
})
}
}
I have an API call in api.js:
export const getGraphData = (domain, userId, testId) => {
return axios({
url: `${domain}/api/${c.embedConfig.apiVersion}/member/${userId}/utests/${testId}`,
method: 'get',
});
};
I have a React helper that takes that data and transforms it.
import { getGraphData } from './api';
const dataObj = (domain, userId, testId) => {
const steps = getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
console.log(steps);
// const steps = test.get('steps');
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) { elemName = element.get('caption'); }
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
};
export default dataObj;
The issue is steps, when logged outside the .then() returns a Promise {<pending>}. If I log results.attributes inside the .then(), I see the data fully returned.
You need to wait until your async call is resolved. You can do this by chaining on another then:
getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
})
.then(steps => {
// put the rest of your method here
});
You can also look at async/await if your platform supports it which would allow code closer to your original
const steps = await getGraphData(domain, userId, testId)
.then((result) => {
return result.attributes;
});
// can use steps here
You have 2 options to transform your fetched data :
1st option : create a async function that returns a promise with the modified data :
const dataObj = (domain, userId, testId) => {
return getGraphData(domain, userId, testId).then((result) => {
const steps = result.attributes;
const expr = /select/;
// build array of steps that we have results in
const resultsSteps = [];
steps.forEach((step) => {
// check for types that contain 'select', and add them to array
if (expr.test(step.get('type'))) {
resultsSteps.push(step);
}
});
const newResultsSteps = [];
resultsSteps.forEach((item, i) => {
const newMapStep = new Map();
const itemDescription = item.get('description');
const itemId = item.get('id');
const itemOptions = item.get('options');
const itemAnswers = item.get('userAnswers');
const newOptArray = [];
itemOptions.forEach((element) => {
const optionsMap = new Map();
let elemName = element.get('value');
if (!element.get('value')) {
elemName = element.get('caption');
}
const elemPosition = element.get('position');
const elemCount = element.get('count');
optionsMap.name = elemName;
optionsMap.position = elemPosition;
optionsMap.value = elemCount;
newOptArray.push(optionsMap);
});
newMapStep.chartType = 'horizontalBar';
newMapStep.description = itemDescription;
newMapStep.featured = 'false';
newMapStep.detailUrl = '';
newMapStep.featuredStepIndex = i + 1;
newMapStep.id = itemId;
newMapStep.isValid = 'false';
newMapStep.type = 'results';
const listForNewOptArray = List(newOptArray);
newMapStep.data = listForNewOptArray;
newMapStep.userAnswers = itemAnswers;
newResultsSteps.push(newMapStep);
});
return newResultsSteps;
});
};
With es7 async/await syntax it should be :
const dataObj = async (domain, userId, testId) => {
const result = await getGraphData(domain, userId, testId);
const steps = result.attributes;
... modify the data
}
Then keep in mind that this function returns a promise, you'll need to wait for it to get the result, example in a react component :
componentDidMount(){
dataObj('mydomain', 'myuserId', 'mytestId').then((res) => {
this.setState({ data: res });
}
}
The component will update when the promise is resolve, you can then use the data (you'll need to handle the undefined data state in render method)
2nd option : Create a sync function to modify the data :
const dataObj = (steps) => {
const expr = /select/;
const resultsSteps = [];
steps.forEach((step) => {
...
}
return newResultsSteps;
};
To have the same result as option 1 in our component we'll use it like this :
componentDidMount(){
getGraphData('mydomain', 'myuserId', 'mytestId').then((res) => {
const modifiedData = dataObj(res);
this.setState({ data: modifiedData });
}
}
That's how promises work. The data is not ready when you are trying to use it so you should move all your processing into the .then. The reason your variable is a Promise {<pending>} is because you can chain other things onto it.
Something like:
steps.then((steps) => {
...
});