Get request using loop - arrays

There was such a problem.
I'm trying to make a certain number of GET requests in Wikipedia API using a cycle.
Trying to do this with the function getAllInfo()
articles.components.ts
export class ArticlesComponent {
constructor(private articlesServices: ArticlesService) { }
#Input() allTitles: string[];
articlesInfo: ArticleInformationNew;
allArray: [[string, number]] = [['', 0]];
static getUrlInformation(searchQuery: string) {
return 'https://ru.wikipedia.org/w/api.php?action=query&titles='
+ searchQuery + '&prop=info&format=json&origin=*';
}
getAllInfo() {
for (const title of this.allTitles) {
this.articlesServices.getArticleInformation(ArticlesComponent.getUrlInformation(title))
.subscribe(
(data: ArticleInformation) => this.articlesInfo = {
...data,
query: { pages: [Object.values(data.query.pages)[0]]}}
);
this.allArray.push([this.articlesInfo.query.pages[0].touched, this.articlesInfo.query.pages[0].length]);
}
}
}
articles.service.ts
export interface ArticleInformation {
batchComplete: string;
query: {
pages: {
}
};
}
export interface ArticleInformationNew {
batchComplete: string;
query: {
pages: any[]
};
}
export class ArticlesService {
constructor(private http: HttpClient) { }
getArticleInformation(url) {
return this.http.get<ArticleInformation>(url);
}
}
An array this.allTitles can consist of a number of lines. For example: this.allTitles = ['Naumen', 'Naumen DMS']
I expect that the arraythis.allArray will be two-dimensional and contain arrays that consist of rows with data for each query. For example:
this.allArray[0] = ['', 0]
this.allArray[1] = ['2019-02-01T23:27:26Z', 3687]
this.allArray[2] = ['2019-01-21T04:24:21Z', 9704]
But in fact, it turns out that each element of a two-dimensional array is the same. For example:
this.allArray[0] = ['', 0]
this.allArray[1] = ['2019-02-01T23:27:26Z', 3687]
this.allArray[2] = ['2019-02-01T23:27:26Z', 3687]
Why and how to fix it?

Try this,
getAllInfo() {
for (const title of this.allTitles) {
this.articlesServices.getArticleInformation(ArticlesComponent.getUrlInformation(title))
.subscribe(
(data: ArticleInformation) => {
this.articlesInfo = {
...data,
query: { pages: [Object.values(data.query.pages)[0]]}
}
this.allArray.push([this.articlesInfo.query.pages[0].touched,this.articlesInfo.query.pages[0].length]);
}
);
}
}

You can use combineLatest: https://www.learnrxjs.io/operators/combination/combinelatest.html
First, collect observables to be combined (but without subscribing to them), then combine them with combineLatest and get the response as an array and iterate over it.
getAllInfo() {
console.log(this.allTitles);
observablesToSubscribe = [];
for (const title of this.allTitles) {
observablesToSubscribe.push(this.articlesServices.getArticleInformation(ArticlesComponent.getUrlInformation(title)));
}
combineLatest(observablesToSubscribe).subscribe((responseData: Array<ArticleInformation>) => {
responseData.forEach((responseDatum) => {
this.allArray.push({
...data,
query: { pages: [Object.values(data.query.pages)[0]]}
})
});
});
}

Maybe, I misunderstood the question, but you can get pages with particular titles, by correcting your searchQuery (using alternative separators for titles), and get rid of for loop:
getAllInfo() {
console.log(this.allTitles);
this.articlesServices.getArticleInformation(
ArticlesComponent.getUrlInformation(this.allTitles.join('|'))
.subscribe(
(res => {
// here is a result with all pages of particular titles,
// then you can process your result...
console.log(res);
// this.allArray is a multidimensional array
// [["2019-01-25T00:45:06Z",4508],
// ["2019-01-26T07:25:08Z", 773]]
this.allArray = Object.keys(res.query.pages)
.reduce((acc, val, index) => {
acc[index] = [pages[val].touched, pages[val].length];
return acc;
}, []);
});
}
searchQuery for titles will be in that case Naumen | Naumen DMS (not, for example, just Naumen). | (pipe) is an alternative separator for titles.
To process the result (res.query.pages):
const pages = {
"755288": {
"pageid": 755288,
"ns": 0,
"title": "Spring",
"contentmodel": "wikitext",
"pagelanguage": "ru",
"pagelanguagehtmlcode": "ru",
"pagelanguagedir": "ltr",
"touched": "2019-01-26T07:25:08Z",
"lastrevid": 84434967,
"length": 773
},
"92586": {
"pageid": 92586,
"ns": 0,
"title": "Atom",
"contentmodel": "wikitext",
"pagelanguage": "ru",
"pagelanguagehtmlcode": "ru",
"pagelanguagedir": "ltr",
"touched": "2019-01-25T00:45:06Z",
"lastrevid": 95248014,
"length": 4508
},
};
const arr = Object.keys(pages).reduce((acc, val, index) => {
acc[index] = [pages[val].touched, pages[val].length];
return acc;
}, []);
console.log(arr);

Related

Expected BEGIN_ARRAY but was BEGIN_OBJECT how to format Object to Array?

{
"workingHours":
[
{
"date":"2023-02-01",
"amount":3,
"freigegeben":false
}
]
}
When I sent this in my request Body, I get the error in my title. How can I manually add [ and ], so it is an Array? Or how could I solve this?
This is the request I send:
public async saveWorkingHours(
employeeId: string | null,
workingHours: WorkingHours[]
): Promise<boolean> {
var result = Object.entries(workingHours.map(wh => ({ ...wh, date: this.dateService.format(wh.date) })));
try {
await this.httpService.fetch(
`${this.apiUrl}employees/${employeeId}/workingHours`,
HttpMethod.PUT,
{
// This is my body, which starts with "{", instead of "[". That is the problem
workingHours: workingHours.map(wh => ({ ...wh, date: this.dateService.format(wh.date) })),
}
);
return true;
} catch (e) {
console.error(e);
return false;
}
}
Workinghours interface:
export interface WorkingHours {
date: Date;
amount: number;
freigegeben: boolean;
}
Doing so:
var convertedFormatData = [];
var tempObj = {
workingHours: workingHours.map((wh) => ({
...wh,
date: this.dateService.format(wh.date),
})),
};
convertedFormatData.push(tempObj);
My Output looks like this:
{"convertedFormatData":[{"workingHours":[{"date":"2023-02-01","amount":3,"freigegeben":false},{ ...
The BEGIN_ARRAY is [, and BEGIN_OBJECT is {.
Here, you are starting with {, so for the website you seems to send an object, but he's waiting for a list. To send a list, you should do:
[
{
"here": "is the json"
}
]
So, for you, it should be something like this:
[
{
"workingHours":[
{
"date":"2023-02-01",
"amount":3,
"freigegeben":false
}
]
}
]
There is few ways to make it as array:
var test = {
"workingHours":[
{
"date":"2023-02-01",
"amount":3,
"freigegeben":false
}
]
};
console.log(test["workingHours"]);

Push value of arrivalDate in array

I would like to store every arrivalDate in my array list.
Someone could tell me how can I do it?
But my array is still empty.
JSON returned by the API:
{
"reservations": {
"reservationInfo": [
{
"roomStay": {
"arrivalDate": "11am"
},
"WeatherR": {
"sound": "cloudy"
},
},
{
"roomStay": {
"arrivalDate": "7pm"
},
"WeatherR": {
"sound": "cloudy"
},
}
]
}
}
component.ts
searchForReservation() {
alert('hello');
this.http.get('/api/searchForReservation')
.subscribe((data) => {
this.ddataIno = data;
this.ddataIno = this.ddataIno.result.reservations.reservationInfo;
console.log('number of value', this.ddataIno.length);
console.log('content', this.ddataIno);
for (let i = 0; i <= this.ddataIno[i].length; i++) {
this.list = this.ddataIno.roomStay.arrivalDate;
}
console.log('store array', this.list)
})
}
searchForReservation() {
alert('hello');
this.http.get('/api/searchForReservation')
.subscribe((data) => {
const reservationInfo = this.data.result.reservations.reservationInfo;
this.list = reservationInfo.map(e => e.roomStay.arrivalDate);
})
}
Here's a working example in vanilla JS. You would need to make some small adjustments for angular, like this.list = ... instead of let list = ...
Using Array#map, you can create a new array from the JSON object
data.reservations.reservationInfo.map(r => r.roomStay.arrivalDate)
let data = {
"reservations": {
"reservationInfo": [{
"roomStay": {
"arrivalDate": "11am"
},
"WeatherR": {
"sound": "cloudy"
},
},
{
"roomStay": {
"arrivalDate": "7pm"
},
"WeatherR": {
"sound": "cloudy"
},
}
]
}
}
// declare your list as an array at the top
// list: []
// below would start off as 'this.list'
let list = data.reservations.reservationInfo.map(r => r.roomStay.arrivalDate);
console.log(list);
Your for loop is just reassigning the value of this.list
I suggest reading up on Array methods
I would use a map method, e.g.
this.list = this.ddataIno.result.reservations.reservationInfo.map(i => i.roomStay.arrivaldate);

Update Object Inside the Array of object in a mongoose document with projection or select

I am trying to update the object inside the document
Document: Cats
{
"_id": "5e5cb512e90bd40017385305",
"type": "cat"
"history": [
{
"id": "randomID",
"content": "xyz",
},
{
"id": "randomID2",
"content": "abc",
}
]
}
Code to select and update the object inside the history array:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true,
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Input has following values
input: {
catId: "5e5cb512e90bd40017385305",
historyId: "randomID",
history: {
id: "randomID",
content: "new content"
}}
I tried using Projection, I used select changed it to field, found in mongoose documentation.
I still couldn't update the values. Is there anything wrong with the way i am querying or selecting the subfield.
Found the Solution for it by going through more detail of the operator($set) and option(new, fields).
Question:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
// using new option would return the new document
new: true,
/* using fields option would select the based on the given key, but using new:
true with fields will throw error: 'cannot use a positional projection and
return the new document'
*/
fields: { history: { $elemMatch: { id: "randomID" } } }
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
This post below answers that question for *error: 'cannot use a positional projection and return the new document'.
https://stackoverflow.com/a/46064082/5492398
Final Solution:
const editHistory = async (_, { input }, ctx) => {
let query = { _id: input.catId, "history.id": input.historyId };
let update = { $set: { "history.$": input.history } };
let options = {
new: true
};
let cat = await ctx.models.cats.findOneAndUpdate(query, update, options);
return cat;
};
Removing field option, since I don't need the unmodified selection before atomic modification, solves the question.

Reducing nested array

I am receiving this JSON from my backend and i need to work out the count of "concrete_compressive_cylinder_100"'s where picked_up = false
concrete_samples (can be multiple per work order) can be null ( key is always present )
sample_specimens ( 1 per concrete_sample) can be null ( key is always present )
concrete_compressive_cylinder_100 ( null to 500 per sample_specimens )
{
"uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7",
"org_workorder_id":1000,
"concrete_samples":[
{
"uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"workorder_uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7",
"org_sample_id":5001,
"sample_specimens":{
"concrete_compressive_cylinder_100":[
{
"uuid":"b9ef3a8a-2945-41e6-a34d-d90d1bd64819",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
},
{
"uuid":"d43f15b3-2208-43de-8fff-8d237c6918f9",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
},
{
"uuid":"472f832a-6f07-4af6-97ea-e6dc7b9b3799",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
}
],
"concrete_compressive_cylinder_200":[
{
"uuid":"d659d058-e4ec-4f72-9d73-9ea98295715a",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
},
{
"uuid":"777372e0-3e58-4292-bae4-bec84dfe1402",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
},
{
"uuid":"f63f7102-7673-4e71-97e5-2d85e0c1a93d",
"sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b",
"picked_up":true
}
]
}
},
{
"uuid":"61138cf3-0c49-4495-8a89-533c0a6e50bc",
"workorder_uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7",
"org_sample_id":5002,
"sample_specimens":{
"concrete_compressive_cylinder_100":null,
"concrete_compressive_cylinder_200":null
}
}
]
}
I've gotten this far but it dosen't really work and now im more confused some guidance would be great
const out = res.data.concrete_samples.reduce((acc, sample) => {
const { sample_specimens } = sample;
const concrete_compressive_cylinder_100 = Object.keys(sample_specimens)["concrete_compressive_cylinder_100"];
const specimens = concrete_compressive_cylinder_100.map(obj => {
obj.picked_up ? console.log("picked up") : console.log("Not pickedn up")
});
}, []);
Array.prototype.reduce accepts a function whose return value is eventually returned from reduce itself. The function is passed each element of the array, along with the value accumulated so far. For example,
[1, 2, 3].reduce((accumulator, element) => accumulator + element)
// => 6
You can also provide an initial value, which will be passed to your function as accumulator on the first iteration.
At a basic level, to count how many occurrences of an object with a certain property with reduce, you could use something like this,
let array = [
{ foo: 4 },
{ foo: 6 },
{ bar: 8 },
]
array.reduce((count, element) => {
if (element.foo !== undefined) {
return count + 1
} else {
return count
}
}, 0)
// => 2
Extending this to your code (with extraneous data elided), with a nested reduce to get the count of cylinders with the desired picked_up property,
const data = {
"concrete_samples":[
{
"sample_specimens":{
"concrete_compressive_cylinder_100":[
{
"picked_up":true
},
{
"picked_up":true
},
{
"picked_up":true
}
],
"concrete_compressive_cylinder_200":[
{
"picked_up":true
},
{
"picked_up":true
},
{
"picked_up":true
}
]
}
},
{
"sample_specimens":{
"concrete_compressive_cylinder_100":null,
"concrete_compressive_cylinder_200":null
}
}
]
}
const result = data.concrete_samples.reduce((count, sample) => {
const cylinders = sample.sample_specimens.concrete_compressive_cylinder_100
if (cylinders == null) {
return count
}
const samplePickedUpCount = cylinders.reduce((pickedUpCount, cylinder) => {
if (cylinder.picked_up) {
return pickedUpCount + 1
} else {
return pickedUpCount
}
}, 0)
return count + samplePickedUpCount
}, 0)
console.log(result)
You could also use Array.prototype.filter to accomplish the same thing, getting an array of the cylinders with the desired property, and getting the length of that array.
If I understand correctly, you'd like to obtain a new concrete_samples array where the array values of nested sample_specimens objects are reduced to the number of items where picked_up is true - one approach to that would be as documented in the following snippet:
const data={"uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7","org_workorder_id":1000,"concrete_samples":[{"uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","workorder_uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7","org_sample_id":5001,"sample_specimens":{"concrete_compressive_cylinder_100":[{"uuid":"b9ef3a8a-2945-41e6-a34d-d90d1bd64819","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true},{"uuid":"d43f15b3-2208-43de-8fff-8d237c6918f9","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true},{"uuid":"472f832a-6f07-4af6-97ea-e6dc7b9b3799","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true}],"concrete_compressive_cylinder_200":[{"uuid":"d659d058-e4ec-4f72-9d73-9ea98295715a","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true},{"uuid":"777372e0-3e58-4292-bae4-bec84dfe1402","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true},{"uuid":"f63f7102-7673-4e71-97e5-2d85e0c1a93d","sample_uuid":"776a8ccb-82fd-4a42-a6eb-8f286a4a9c0b","picked_up":true}]}},{"uuid":"61138cf3-0c49-4495-8a89-533c0a6e50bc","workorder_uuid":"4ad7bfe1-48d6-488c-bfaf-33f7189a41d7","org_sample_id":5002,"sample_specimens":{"concrete_compressive_cylinder_100":null,"concrete_compressive_cylinder_200":null}}]};
const concreteSamplesResult = data.concrete_samples.map(sample => {
// Iterate each key/value entry of sample_specimens, and reduce to a new
// specimens object that contains counts of picked_up: true items in sub array
const sample_specimens = Object
.entries(sample.sample_specimens)
.reduce((specimens, entry) => {
// Calculate count of picked_up items for arr of this entry
const [key, arr] = entry;
const count = Array.isArray(arr) ?
arr.reduce((total, item) => (total + (item.picked_up ? 1 : 0)), 0) : 0;
// Add count for entry key to newly reduced sample_specimen object
return { ...specimens, [key] : count };
},{})
return { ...sample, sample_specimens };
});
console.log(concreteSamplesResult);
Check null > loop > check null > loop and count isn't it?
function someFunc(json) {
const { concrete_samples } = json;
if (!concrete_samples) return;
let numberOFAvailableCylinder100s = 0;
const doSomethingWithCylinder = cylinder => {
console.log(cylinder.uuid);
numberOFAvailableCylinder100s += 1;
}
concrete_samples.forEach(concrete_sample => {
const { sample_specimens } = concrete_sample;
if (!sample_specimens) return;
findAvailableCylinder100(sample_specimens, doSomethingWithCylinder);
})
console.log(`count: ${numberOFAvailableCylinder100s}`);
}
function findAvailableCylinder100(sample_specimens, callback) {
const { concrete_compressive_cylinder_100 } = sample_specimens;
if (!concrete_compressive_cylinder_100) return;
concrete_compressive_cylinder_100.forEach(cylinder => {
if (!cylinder.picked_up) callback(cylinder);
});
}
someFunc(yourJSONObject);

graphql: how to handle prev page, next page, last page and first page properly?

For frontend I am using React + Relay. I have some connection at the backend that could be queried like:
query Query {
node(id: 123456) {
teams(first: 10) {
node {
id
name
}
page_info {
start_cursor
end_cursor
}
}
}
}
So for the traditional approach, I can use skip PAGE_SIZE * curr limit PAGE_SIZE to query for next page, prev page and first page and last page (In fact I can query for random page)
But how should I implement the frontend to make these requests elegantly?
#Junchao, what Vincent said is correct. Also, you must have a re-fetch query and send refetchVariables with your first value updated. I will try to provide you an example:
export default createRefetchContainer(
TeamsComponent,
{
query: graphql`
fragment TeamsComponent_query on Query
#argumentDefinitions(
first: { type: Int }
last: { type: Int }
before: { type: String }
after: { type: String }
) {
teams(
id: { type: "ID!" }
first: { type: Int }
last: { type: Int }
before: { type: String }
after: { type: String }
) #connection(key: "TeamsComponent_teams", filters: []) {
count
pageInfo {
endCursor
hasNextPage
}
edges {
node {
id
name
}
}
}
}
`,
},
graphql`
query TeamsComponent(
$after: String
$before: String
$first: Int
$last: Int
) {
...TeamsComponent_query
#arguments(
first: $first
last: $last
after: $after
before: $before
)
}
`,
);
I tried to build an example based on your code. This is basically the idea. The bottom query is the re-fetch one. Alongside with that, you must trigger this re-fetch somehow by calling this.props.relay.refetch passing your renderVaribles. Take a deep looker into the docs about this.
Hope is helps :)
UPDATE:
Just to add something, you could have a handleLoadMore function with something like this:
handleLoadMore = () => {
const { relay, connection } = this.props;
const { isFetching } = this.state;
if (!connection) return;
const { edges, pageInfo } = connection;
if (!pageInfo.hasNextPage) return;
const total = edges.length + TOTAL_REFETCH_ITEMS;
const fragmentRenderVariables = this.getRenderVariables() || {};
const renderVariables = { first: total, ...fragmentRenderVariables };
if (isFetching) {
// do not loadMore if it is still loading more or searching
return;
}
this.setState({
isFetching: true,
});
const refetchVariables = fragmentVariables => ({
first: TOTAL_REFETCH_ITEMS,
after: pageInfo.endCursor,
});
relay.refetch(
refetchVariables,
null,
() => {
this.setState({ isFetching: false });
},
{
force: false,
},
);
};
UPDATE 2:
For going backwards, you could have something like:
loadPageBackwardsVars = () => {
const { connection, quantityPerPage } = this.props;
const { quantity } = getFormatedQuery(location);
const { endCursorOffset, startCursorOffset } = connection;
const firstItem = connection.edges.slice(startCursorOffset, endCursorOffset)[0].cursor;
const refetchVariables = fragmentVariables => ({
...fragmentVariables,
...this.getFragmentVariables(),
last: parseInt(quantity || quantityPerPage, 10) || 10,
first: null,
before: firstItem,
});
return refetchVariables;
};

Resources