How to use map function on a json response in javascript? - javascript

I am using zomato API to find a random restaurant within an area. This is the API call.
app.post('/locations/:query', async (req, res) =>
{
try{
const query = req.params.query;
const data = await zomato.cities({ q: query,count: 1 })
const cityId= await (data[0].id);
const result = [];
const nrOfRequests = 60;
let currCount = 0;
const nrOfEntries = 20;
for(let i=0; i < nrOfRequests ; i++) {
const response = await zomato.search({ entity_id: cityId, entity_type: 'city', start:currCount, count:nrOfEntries, sort:'rating', order:'desc' });
result.push(...response.restaurants);
currCount += nrOfEntries;
}
const no = Math.floor(Math.random() * 60);
const restaur = result[no].restaurants.map(r => {
return {
name: r.name,
url: r.url,
location: r.location,
price: r.price_range,
thumbnail: r.thumb,
rating: r.user_rating.aggregate_rating,
}
})
res.send({restaur});
} catch (err) {
console.error(err)
res.status(500).send('error')
}
});
What this API is doing is
1)Finds cityId of query.(query is a city name)
2)Using for loop finds upto 60 restaurants in that city according to rating and pushes it in results[].(zomato api has restriction to display only 20 restaurants per call)
3)Chooses a random number between 0 to 60 and outputs that particular restaurant from result.
4)Then what I want to do is find features like name price etc and return to display that in the frontend.
Using this code I get the following error
TypeError: Cannot read property 'map' of undefined
The result[no] response looks like this.
{
R: {
has_menu_status: { delivery: -1, takeaway: -1 },
res_id: 302578,
is_grocery_store: false
},
apikey: '*************',
id: '302578',
name: 'Barbeque Nation',
url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1',
location: {
address: 'M/A 04, DLF Avenue, District Centre, Saket, New Delhi',
locality: 'DLF Avenue, Saket',
city: 'New Delhi',
city_id: 1,
latitude: '28.5273432339',
longitude: '77.2173847631',
zipcode: '',
country_id: 1,
locality_verbose: 'DLF Avenue, Saket, New Delhi'
},
switch_to_order_menu: 0,
cuisines: 'North Indian, BBQ, Beverages',
timings: '12noon – 3:30pm, 6pm – 11pm (Mon-Sun)',
average_cost_for_two: 1600,
price_range: 3,
currency: 'Rs.',
highlights: [
'Lunch',
'Serves Alcohol',
'Mall Parking',
'Cash',
'Credit Card',
'Debit Card',
'Dinner',
'Takeaway Available',
'Fullbar',
'Indoor Seating',
'Air Conditioned',
'Reopened',
'Buffet'
],
offers: [],
opentable_support: 0,
is_zomato_book_res: 0,
mezzo_provider: 'OTHER',
is_book_form_web_view: 0,
book_form_web_view_url: '',
book_again_url: '',
thumb: 'https://b.zmtcdn.com/data/pictures/chains/2/1212/b6429ddad24625e65344caabb921bd57.jpg?fit=around%7C200%3A200&crop=200%3A200%3B%2A%2C%2A',
user_rating: {
aggregate_rating: '4.7',
rating_text: 'Excellent',
rating_color: '3F7E00',
rating_obj: { title: [Object], bg_color: [Object] },
votes: 2248
},
all_reviews_count: 1159,
photos_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/photos?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1#tabtop',
photo_count: 1991,
menu_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/menu?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1&openSwipeBox=menu&showMinimal=1#tabtop',
featured_image: 'https://b.zmtcdn.com/data/pictures/chains/2/1212/b6429ddad24625e65344caabb921bd57.jpg',
medio_provider: 1,
has_online_delivery: 1,
is_delivering_now: 0,
store_type: '',
include_bogo_offers: true,
deeplink: 'zomato://restaurant/302578',
is_table_reservation_supported: 1,
has_table_booking: 0,
events_url: 'https://www.zomato.com/ncr/barbeque-nation-saket-new-delhi/events#tabtop?utm_source=api_basic_user&utm_medium=api&utm_campaign=v2.1',
phone_numbers: '080 61756005',
all_reviews: { reviews: [ [Object], [Object], [Object], [Object], [Object] ] },
establishment: [ 'Casual Dining' ],
establishment_types: []
}

The results[no] that you're showing indeed has no property called restaurants. You have an array called results, and you're selecting a single element from that array. When you have a single element, there's nothing to "map".
Instead, just return the fields you want from that element:
const no = Math.floor(Math.random() * 60);
const restaur = {
name: result[no].name,
url: result[no].url,
location: result[no].location,
price: result[no].price_range,
thumbnail: result[no].thumb,
rating: result[no].user_rating.aggregate_rating,
};
res.send(restaur);

Related

How to search nested property in mongoose

hi I have mongodb document like below:
{
_id: ObjectId("63df40c651f1358f2b60f24a"),
origin: {
country: 'Usa',
state: 'Washington',
city: 'Washington',
type: 'Point',
coordinates: [ 12.555, 18.645 ]
},
destination: {
country: 'Usa',
state: 'Montana',
city: 'Yellowstone',
type: 'Point',
coordinates: [ 12.555, 20.645 ]
},
date: 'mon 2023-12-16',
seats: 4,
status: 'pending',
driver: {
firstName: 'sam',
lastName: 'johnson',
phoneNumber: '92823831736',
socialNumber: '1381222327',
driverId: '63d8f94202653dc3592a0aa4'
},
I use below url and code for finding location base on destination:
URL: "localhost:3000/api/v1/travel/?state=Montana&city=Yellowstone"
and my code is:
const Travel = require("../models/travel-model");
const getAllTravelsByDestination = async (req, res) => {
const { country, state, city } = req.query;
const queryObject = {};
if (country) {
queryObject.destination.country = country;
}
if (state) {
queryObject.destination.state= state;
}
if (city) {
queryObject.destination.city = city;
}
const travelList = await Travel.find({ destination:queryObject });
res.status(StatusCodes.OK).json({ status: "success", travelList });
};
I expect to get my document back but instead i get empty list as response:
{
"status": "success",
"travelList": []
}
please help me for this problam
You created a filter properly. But, instead of find({ destination: queryObject }), you should only do find(queryObject), since the queryObject already contains the destination property.
const travelList = await Travel.find(queryObject);
Check this line of your code:
const travelList = await Travel.find({ destination:queryObject });
You defined destination already in the queryObject. So you should just do
await Travel.find(queryObject);
Please note that queries are by default case-sensitive. So upper & lowercase matters.

Reduce function works but takes too long, vue

In my Vue app, I have a reduce function that's being called within an html/vue loop and it's taking far too long; around 21 seconds
During that time, nothing renders and the page freezes temporarily
I think part of the issue is that I'm calling the computed property in a loop and it calls the reduce function each time, but I'm still unclear on a way to optimize this to quickly go through the reduce function and allow the loop to only hit the result set as opposed to reducing through each iteration.
My result set is about 12,000 records but I've only included a few in the exact structure.
What can I do here?
<script>
const reduceFunction = (rows) =>
rows .reduce(
(a, row) => {
const employee = a [row .employee] || (a [row .employee] = {dates: {}, total_categories:0, total_items: 0, area: '', group: ''})
const date = employee .dates [row .itemDate] || (employee .dates [row .itemDate] = {categories: 0, qty: 0, total_categories: 0, unavailable: 0, orders: {}})
date.categories += +row.categories_per_item * +row.qty
date.qty += +row.qty
date.total_categories = date.categories
const order = date .orders [row .order_number] || (date .orders [row .order_number] = {itemDate: '', skus: {}})
order.itemDate = row.itemDate;
const sku = order .skus [row .sku] || (order .skus [row .sku] = {categories: '', qty: '', itemDate: '', expected: '', created: '', unavailable: 0, available:0})
sku.categories += row.categories_per_item
sku.qty += row.qty
sku.itemDate = row.itemDate
sku.expected = row.shipDate
sku.created = row.created_date
sku.heir_id = row.heir_identifier
employee.total_categories += (+row.categories_per_item * +row.qty)
employee.total_items += (+row.qty)
employee.area = row.area
employee.group = row.group_name
employee.warehouse = row.warehouse
employee.locale = row.locale
const foundKit = vm.$data.kitsData.find((kit) => kit.heir_identifier === sku.heir_id)
if (foundKit) {
new_avail = 10;
if(sku.qty > new_avail){
status.status = "Not available";
date.unavailable += 1
sku.unavailable += 1
}else{
status.status = "Available"
}
}else{
status.status = "No item found"
}
return a
},
{}
);
var vm =
new Vue({
el: "#app",
data: {
rows: [
{
employee: "Adam",
sku: "A1453",
categories_per_item: "15",
area: "1",
itemDate: "2021-11-02",
qty: 37,
group_name: "managers",
warehouse: "3",
order_number: "1234",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "Joan",
sku: "A1453",
categories_per_item: "15",
area: "1a",
itemDate: "2021-11-02",
qty: 17,
group_name: "managers",
warehouse: "3",
order_number: "34578",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "Bill",
sku: "A1453",
categories_per_item: "15",
area: "1",
itemDate: "2021-11-03",
qty: 57,
group_name: "managers",
warehouse: "3",
order_number: "2345",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC3"
},
{
employee: "PJ",
sku: "A6512",
categories_per_item: "150",
area: "2",
itemDate: "2021-11-03",
qty: 20,
group_name: "managers",
warehouse: "3",
order_number: "34567",
locale: "1",
shipDate: "2020-02-02",
created_date: "2020-01-01",
heir_identifier:"ABC1"
}
]
},
methods: {
},
computed: {
employeeData() {
console.log('employee data')
employeeRows = reduceFunction(this.rows)
return employeeRows
console.log(employeeRows)
},
dates() {
return Array.from(Array(11), (_, i) => new Date(Date.now() + i * 86400000).toISOString().slice(0,10))
}
}
});
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<tr v-for="(value, employee) in employeeData" :key="employee">
<td>#{{employee}}</td>
<td v-for="date in dates" :key="date" >
<div v-for="(dateInfo, dateValue) in value.dates" :key="dateValue" >
<div v-if="dateValue == date ">
#{{ dateInfo.total_categories }}
</div>
</div>
</td>
</tr>
</div>
My approach for this problem would be to invoke reduceFunction on mounted(){}
and create another state for the array, here I called it parsedRows
So basically to avoid unnecessary re rendering.
data: {
rows: []
parsedRows: []
}
methods: {
reduceFunction(data){
//adjust your code to fit method here
}
}
mounted(){
this.parsedRows = this.reduceFunction(this.rows);
}
and then use the parsedRows on the Vue template.
Also to move the reduceFunction to methods
The main improvement I could see would be to eliminate the nested loop here:
const foundKit = vm.$data.kitsData.find((kit) => kit.heir_identifier === sku.heir_id)
Organize the kitsDat by heir_identifier first so you can look up in O(1) instead of .finding (O(n)) each time.
const kitsByHeir = new Map();
for (const kit of vm.$data.kitsData) {
kitsByHeir.set(kit.heir_identifier, kit);
}
Then do kitsByHeir.get(sku.heir_id) inside the loop.
You might also use a for loop instead of reduce (reduce is arguably not appropriate in this situation anyway)
Also, processing 12,000 records on the client-side is pretty odd. Even with the best designed code, that could take an uncomfortable amount of time in certain environments. Consider moving the processing to a server instead.

Counting occurrence of values in an array of objects in another array of objects

I need to create a Graphql query that outputs data from two arrays of objects. The arrays are:
const authors = [
{
name: 'Robert Martin',
id: 'afa51ab0-344d-11e9-a414-719c6709cf3e',
born: 1952
},
{
name: 'Martin Fowler',
id: 'afa5b6f0-344d-11e9-a414-719c6709cf3e',
born: 1963
},
{
name: 'Fyodor Dostoevsky',
id: 'afa5b6f1-344d-11e9-a414-719c6709cf3e',
born: 1821
},
{
name: 'Joshua Kerievsky', // birthyear not known
id: 'afa5b6f2-344d-11e9-a414-719c6709cf3e'
},
{
name: 'Sandi Metz', // birthyear not known
id: 'afa5b6f3-344d-11e9-a414-719c6709cf3e'
}
];
And:
const books = [
{
title: 'Clean Code',
published: 2008,
author: 'Robert Martin',
id: 'afa5b6f4-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Agile software development',
published: 2002,
author: 'Robert Martin',
id: 'afa5b6f5-344d-11e9-a414-719c6709cf3e',
genres: ['agile', 'patterns', 'design']
},
{
title: 'Refactoring, edition 2',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 3',
published: 2018,
author: 'Martin Fowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring, edition 4',
published: 2018,
author: 'Martin Cowler',
id: 'afa5de00-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring']
},
{
title: 'Refactoring to patterns',
published: 2008,
author: 'Joshua Kerievsky',
id: 'afa5de01-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'patterns']
},
{
title: 'Practical Object-Oriented Design, An Agile Primer Using
Ruby',
published: 2012,
author: 'Sandi Metz',
id: 'afa5de02-344d-11e9-a414-719c6709cf3e',
genres: ['refactoring', 'design']
},
{
title: 'Crime and punishment',
published: 1866,
author: 'Fyodor Dostoevsky',
id: 'afa5de03-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'crime']
},
{
title: 'The Demon ',
published: 1872,
author: 'Fyodor Dostoevsky',
id: 'afa5de04-344d-11e9-a414-719c6709cf3e',
genres: ['classic', 'revolution']
}
];
The desired output format for a query like this:
query {
allAuthors {
name
bookCount
}
}
is like so:
"data": {
"allAuthors": [
{
"name": "Robert Martin",
"bookCount": 2
},
{
"name": "Martin Fowler",
"bookCount": 1
},
{
"name": "Fyodor Dostoevsky",
"bookCount": 2
},
{
"name": "Joshua Kerievsky",
"bookCount": 1
},
{
"name": "Sandi Metz",
"bookCount": 1
}
]
}
I've found a way to count the amount of books for each author and output the data in the desired format (a good example of that here: Summarize count of occurrences in an array of objects with Array#reduce). However this approach ignores other fields in the data, such as "born" and "genres". If I was to expand the query like so:
query {
allAuthors {
name
bookCount
born
}
}
It wouldn't output anything for the field "born". What would be the smart way to create the query resolver? Spread operator? Reduce?
* EDIT *
My unnecessarily complicated solution for counting the books here:
const newBooks = books.reduce((acc, cv) => {
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
arr[0].bookCount += 1;
}
return acc;
}, []);
const array = [];
books.forEach(book => {
const object = {
name: book.author
};
array.push(object);
return array;
});
const unique = array.map(a => a.name);
result = {};
for (var i = 0; i < unique.length; ++i) {
if (!result[unique[i]]) result[unique[i]] = 0;
++result[unique[i]];
}
const entries = Object.entries(result);
const finalAnswer = [];
entries.forEach(entry => {
const object = {
name: entry[0],
bookCount: entry[1]
};
finalAnswer.push(object);
return finalAnswer;
});
console.log(finalAnswer);
You could map the authors and use filter to get the bookCount for each author
const authors=[{name:'Robert Martin',id:'afa51ab0-344d-11e9-a414-719c6709cf3e',born:1952},{name:'Martin Fowler',id:'afa5b6f0-344d-11e9-a414-719c6709cf3e',born:1963},{name:'Fyodor Dostoevsky',id:'afa5b6f1-344d-11e9-a414-719c6709cf3e',born:1821},{name:'Joshua Kerievsky',id:'afa5b6f2-344d-11e9-a414-719c6709cf3e'},{name:'Sandi Metz',id:'afa5b6f3-344d-11e9-a414-719c6709cf3e'}],
books=[{title:'Clean Code',published:2008,author:'Robert Martin',id:'afa5b6f4-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Agile software development',published:2002,author:'Robert Martin',id:'afa5b6f5-344d-11e9-a414-719c6709cf3e',genres:['agile','patterns','design']},{title:'Refactoring, edition 2',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 3',published:2018,author:'Martin Fowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring, edition 4',published:2018,author:'Martin Cowler',id:'afa5de00-344d-11e9-a414-719c6709cf3e',genres:['refactoring']},{title:'Refactoring to patterns',published:2008,author:'Joshua Kerievsky',id:'afa5de01-344d-11e9-a414-719c6709cf3e',genres:['refactoring','patterns']},{title:'Practical Object-Oriented Design, An Agile Primer Using Ruby ',published:2012,author:'Sandi Metz',id:'afa5de02-344d-11e9-a414-719c6709cf3e',genres:['refactoring','design']},{title:'Crime and punishment',published:1866,author:'Fyodor Dostoevsky',id:'afa5de03-344d-11e9-a414-719c6709cf3e',genres:['classic','crime']},{title:'The Demon ',published:1872,author:'Fyodor Dostoevsky',id:'afa5de04-344d-11e9-a414-719c6709cf3e',genres:['classic','revolution']}];
const output = authors.map(({ born, name }) => {
const bookCount = books.filter(b => b.author === name).length;
return { name, born, bookCount }
})
console.log(output)
I think you can add a statement to your reducer function to add the desired fields. I added the single line, and annotated the rest of the method so you can see what's going on:
const newBooks = books.reduce((acc, cv) => {
// acc is an "accumulation" of the results so far.
// cv is the next item that hasn't been processed.
// Search for author in "accumulator" array acc. Put results in arr.
const arr = acc.filter(obj => {
return obj.author === cv.author;
});
if (arr.length === 0) {
// Haven't seen this author, yet. Add new item to "accumulator" array.
acc.push({ name: cv.author, born: cv.born, bookCount: 1 });
} else {
// This author already exists in "accumulator" array, referenced by arr[0].
// Update pre-existing item.
arr[0].bookCount += 1;
arr[0].born = cv.born; // <-- This is the new code that is required.
}
return acc;
}, []);

Load all data from local database

I am using react-native-gifted-chat(https://github.com/FaridSafi/react-native-gifted-chat) to create a chat interface on my app, I want to load messages from my database.
I am using realm, and I am able to load the data but my code below loads only the first row of the data. I want to be able to load all the data from the database.
let chatData = realmDatabase.objects(DatabaseTableNames.chatTable);
let data=[];
for (let message of chatData ){
data = [{
_id: message.chatUniqueID,
text: message.msgBody,
createdAt: (new Date()).getTime(),
user: {
_id: message.chatUniqueID,
name: message.senderName
}
} ]
}
console.log(data)
I want to be able to load all the data from the database not only the first row, like the sample below.
[
{
_id: Math.round(Math.random() * 1000000),
text:
"It uses the same design as React, letting you compose a rich mobile UI from declarative components https://facebook.github.io/react-native/",
createdAt: new Date(Date.UTC(2016, 7, 31, 17, 20, 0)),
user: {
_id: 1,
name: "Developer"
},
},
{
_id: Math.round(Math.random() * 1000000),
text: "React Native lets you build mobile apps using only JavaScript",
createdAt: new Date(Date.UTC(2016, 7, 30, 17, 20, 0)),
sent: true,
received: true,
user: {
_id: 2,
name: "Developer"
},
}
];
Doing data = [{...}] in the for loop, will assign the last value of message to data. To get all the values, you need to push the items in the data array. You can do it like this:
let chatData = realmDatabase.objects(DatabaseTableNames.chatTable);
let data=[];
for (let message of chatData ){
data.push({
_id: message.chatUniqueID,
text: message.msgBody,
createdAt: (new Date()).getTime(),
user: {
_id: message.chatUniqueID,
name: message.senderName
}
});
}
console.log(data)

Mongoose find and findOne return entire collection

I have the code below in javascript and the database is already populated. Now when I use either find() or findOne(), Mongoose returns the entire collection of over 6000 entries. Why is the filter not happening?
var tickerIDSchema = new mongoose.Schema({
ticker: String,
name: String,
exchange: String,
"_id": false
});
var tickerListSchema = new mongoose.Schema({
complete: [tickerIDSchema]
});
tickerListSchema.index(
{ "complete.ticker": 1, "complete.exhcange": 1 },
{ unique: true }
);
var tickerList = mongoose.model("tickerList", tickerListSchema);
tickerList.findOne({"complete.ticker": "BMO"}, function(err, data){
console.log(data)
})
Result:
{ _id: 5a44452bb1dac235f039c66c,
__v: 0,
complete:
[ { ticker: 'AAPL', name: 'Apple Inc.', exchange: 'Nasdaq' },
{ exchange: 'Amex',
name: 'Altisource Asset Management Corp',
ticker: 'AAMC' },
{ exchange: 'Amex',
name: 'Almaden Minerals, Ltd.',
ticker: 'AAU' },
{ exchange: 'Amex',
name: 'Aberdeen Emerging Markets Smaller Company Opportunities Fund I',
ticker: 'ABE' },
{ exchange: 'Amex',
name: 'Acme United Corporation.',
ticker: 'ACU' },
{ exchange: 'Amex', name: 'AeroCentury Corp.', ticker: 'ACY' },
{ exchange: 'Amex',
name: 'Adams Resources & Energy, Inc.',
ticker: 'AE' },
{ exchange: 'Amex', name: 'Ashford Inc.', ticker: 'AINC' },
{ exchange: 'Amex',
name: 'Air Industries Group',
ticker: 'AIRI' },
... 6675 more items ] }
You appear to have an extra layer of abstraction that is unnecessary.
This code effectively creates a document with a single attribute.
var tickerListSchema = new mongoose.Schema({
complete: [tickerIDSchema]
});
The result is that you have a single document in your mongodb collection tickerList that contains all of your data within that single attribute, complete. Per the mongodb documentation (https://docs.mongodb.com/manual/reference/method/db.collection.findOne/), findOne should return the matching document. Because you are querying for a subdocument, the returned result is the parent document, which contains all of the data you have in your tickerIDSchema
To achieve your desired results, your mongoose code should probably look something like this.
var tickerIDSchema = new mongoose.Schema({
ticker: String,
name: String,
exchange: String,
"_id": false
});
tickerIDSchema.index(
{ "ticker": 1, "exchange": 1 },
{ unique: true }
);
var tickerList = mongoose.model("tickerList", tickerIDSchema);
tickerList.findOne({"ticker": "BMO"}, function(err, data){
console.log(data)
})
Removing the tickerListSchema will allow you to query the tickerIDSchema directly. The findOne() query would appear as shown, and find() should operate as follows:
tickerList.find({}, function(err, data){
console.log(data)
})

Categories

Resources