Angular $resource response is not treated as Array - javascript

My server is sending a response with Array inside of Array:
[
[{1:1},{1:2}],
[{2:1},{2:2}]
]
I'm not able to execute array functions on the internal array.
I'm able to execute forEach on the outer array, but not on the internal ones.
Here's my service:
return $resource('api/blablabla/:blablaId', {
blablaId: '#_id'
}, {
'query': {method: 'GET', isArray: true}
});
so far I've tried using:
myData=angular.toJson(myData)
and
myData=JSON.parse(angular.toJson(myData))
But it hasn't worked. In fact, they gave the following output:
{"0":{"1":1},"1":{"1":2}}
angular.forEach worked, but I need some other array functions too.

if you are sure that response data will be array of array list, then you can iterate using below code. there is no need to convert it into json.
a.forEach(function(a1){
a1.forEach(function(item) {
for( var key in item ) {
console.log(item[key])
}
})
})

Related

How to use JSON response as an array in javascript

Here is my response I get from API request:
[ 'bookShelf3', 'bookShelf4', 'bookShelf5' ]
Here is a part of my code which searches through my mongoDB Databese:
const responseToSearchC = (req, res) => {
console.log(req.body.searchTerm);
db.collection('subtitle')
.find({
series: { $in: ['bookShelf1'] },
$text: { $search: req.body.searchTerm },
})
I just want to make my code dynamic and instead hard coding ['bookShelf1']
set its value by JSON response.
the problem is the response from API is an object (although it is look like an array) and I cannot replace it with my hard codded array ['bookShelf1']
I tried to stringify it but it didn't work cuz its a string again, and not an array like hardcoded one
If the response is really an object like:
{ 0: 'bookShelf3', 1:'bookShelf4', 2: 'bookShelf5'}
you can simply utilize the Object.values() method which returns all of the object's values as an array.
Here's an example:
let ob={ 0: 'bookShelf3', 1:'bookShelf4', 2: 'bookShelf5'};
console.log(Object.values(ob));

Skipping top level of JSON data and retrieving data below it via JavaScript

Via a microservice, I retrieve several packages of JSON data and spit them out onto a Vue.js-driven page. The data looks something like this:
{"data":{"getcompanies":
[
{"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"},
{"id":7,"name":"McMillan","address":null,"zip":"15090"},
{"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}
]
}}
{"data":{"getusers":
[{"id":22,"name":"Fred","address":"Parmesean Street","zip":"15090"},
{"id":24,"name":"George","address":"Loopy Lane","zip":"15090"},
{"id":25,"name":"Lucy","address":"Farm Road","zip":"15090"}]}}
{"data":{"getdevices":
[{"id":2,"name":"device type 1"},
{"id":4,"name":"device type 2"},
{"id":5,"name":"device type 3"}]}}
...and I successfully grab them individually via code like this:
getCompanies() {
this.sendMicroServiceRequest({
method: 'GET',
url: `api/authenticated/function/getcompanies`
})
.then((response) => {
if(response.data) {
this.dataCompanies = response.data.getcompanies
} else {
console.error(response)
}
}).catch(console.error)
}
...with getUsers() and getDevices() looking respectively the same. getCompanies() returns:
[{"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"},
{"id":7,"name":"McMillan","address":null,"zip":"15090"},
{"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}]
...which I relay to the Vue template in a table, and this works just fine and dandy.
But this is obviously going to get unwieldy if I need to add more microservice calls down the road.
What I'm looking for is an elegant way to jump past the response.data.*whatever* and get to those id-records with a re-useable call, but I'm having trouble getting there. response.data[0] doesn't work, and mapping down to the stuff I need either comes back undefined, or in bits of array. And filtering for response.data[0].id to return just the rows with ids keeps coming back undefined.
My last attempt (see below) to access the data does work, but looks like it comes back as individual array elements. I'd rather not - if possible - rebuild an array into a JSON structure. I keep thinking I should be able to just step past the next level regardless of what it's called, and grab whatever is there in one chunk, as if I read response.data.getcompanies directly, but not caring what 'getcompanies' is, or needing to reference it by name:
// the call
this.dataCompanies = this.getFullData('companies')
getFullData(who) {
this.sendMicroServiceRequest({
method: 'GET',
url: 'api/authenticated/function/get' + who,
})
.then((response) => {
if(response) {
// attempt 1 to get chunk below 'getcompanies'
Object.keys(response.data).forEach(function(prop) {
console.log(response.data[prop])
})
// attempt 2
// for (const prop in response.data) {
// console.log(response.data[prop])
// }
let output = response.data[prop] // erroneously thinking this is in one object
return output
} else {
console.error(response)
}
}).catch(console.error)
}
...outputs:
(63) [{…}, {…}, {…}] <-- *there are 63 of these records, I'm just showing the first few...*
0: {"id":6,"name":"Arena","address":"12 Baker Street","zip":"15090"}
1: {"id":7,"name":"McMillan","address":null,"zip":"15090"},
2: {"id":8,"name":"Ball","address":"342 Farm Road","zip":"15090"}...
Oh, and the return above comes back 'undefined' for some reason that eludes me at 3AM. >.<
It's one of those things where I think I am close, but not quite. Any tips, hints, or pokes in the right direction are greatly appreciated.
I feel it's better to be explicit about accessing the object. Seems like the object key is consistent with the name of the microservice function? If so:
getData(functionName) {
return this.sendMicroServiceRequest({
method: 'GET',
url: "api/authenticated/function/" + functionName
})
.then( response => response.data[functionName] )
}
getCompanies(){
this.getData("getcompanies").then(companies => {
this.dataCompanies = companies
})
}
let arrResponse = {data: ['x']};
let objResponse = {data: {getcompanies: 'x'}};
console.log(arrResponse.data[0]);
console.log(Object.values(objResponse.data)[0]);
response.data[0] would work if data was an array. To get the first-and-only element of an object, use Object.values(response.data)[0] instead. Object.values converts an object to an array of its values.
Its counterparts Object.keys and Object.entries likewise return arrays of keys and key-value tuples respectively.
Note, order isn't guaranteed in objects, so this is only predictable in your situation because data has exactly a single key & value. Otherwise, you'd have to iterate the entry tuples and search for the desired entry.
firstValue
Let's begin with a generic function, firstValue. It will get the first value of an object, if present, otherwise it will throw an error -
const x = { something: "foo" }
const y = {}
const firstValue = t =>
{ const v = Object.values(t)
if (v.length)
return v[0]
else
throw Error("empty data")
}
console.log(firstValue(x)) // "foo"
console.log(firstValue(y)) // Error: empty data
getData
Now write a generic getData. We chain our firstValue function on the end, and be careful not to add a console.log or .catch here; that is a choice for the caller to decide -
getData(url) {
return this
.sendMicroServiceRequest({ method: "GET", url })
.then(response => {
if (response.data)
return response.data
else
return Promise.reject(response)
})
.then(firstValue)
}
Now we write getCompanies, getUsers, etc -
getCompanies() {
return getData("api/authenticated/function/getcompanies")
}
getUsers() {
return getData("api/authenticated/function/getusers")
}
//...
async and await
Maybe you could spruce up getData with async and await -
async getData(url) {
const response =
await this.sendMicroServiceRequest({ method: "GET", url })
return response.data
? firstValue(response.data)
: Promise.reject(response)
}
power of generics demonstrated
We might even suggest that these get* functions are no longer needed -
async getAll() {
return {
companies:
await getData("api/authenticated/function/getcompanies"),
users:
await getData("api/authenticated/function/getusers"),
devices:
await getData("api/authenticated/function/getdevices"),
// ...
}
}
Above we used three await getData(...) requests which happen in serial order. Perhaps you want all of these requests to run in parallel. Below we will show how to do that -
async getAll() {
const requests = [
getData("api/authenticated/function/getcompanies"),
getData("api/authenticated/function/getusers"),
getData("api/authenticated/function/getdevices")
]
const [companies, users, devices] = Promise.all(requests)
return { companies, users, devices }
}
error handling
Finally, error handling is reserved for the caller and should not be attempted within our generic functions -
this.getAll()
.then(data => this.render(data)) // some Vue template
.catch(console.error)

Axios turns nested JSON array to empty when posting

I'm trying to post data from Axios to my API but although my json data looks correct when passing to Axios, browser tools tells me that my nested property is empty.
My data contains nested objects and looks like this:
{
'name': 'foo',
'index': 1,
'nested': [
{
'date': '2020-05-10',
'geojson_data': {
'crs': Object,
'name': 'my-name',
'type': 'FeatureCollection',
'features': [{ ... }, { ... }]
}
},
]
}
Edit: geojson_data results from parsing .geojson file, thus, the features array contains ~300 items.
My axios function is defined here:
async post(data) {
console.log(data);
return axios
.post(API_URL + 'endpoint/',
data,
{
headers: authHeader()
})
.then(response => {
return response.data;
})
}
authHeader() is used to provide Bearer token authorization.
I check that data passed to axios is correct (looks like above), but browser tools tell me that data actually sent looks like
{"name":"foo","index":1,"nested":[]}
Why is my array empty ?
Edit: I tried to manually populate a sample nested object and it seems to work. Yet, whenever I use my actual data, it doesn't.
Don't know if it's relevant but in the console, here is the difference I can see between the 2 collapsed objects (actual vs sample):
Sample : Object { name: "foo", index: "1", nested: (1) […] }
Actual : Object { name: "foo", index: "1", nested: [] }
Notice the nested array which looks like it is empty. Yet, if I expand it, they look the same. Any ideas ?
P.S: looks like this SO post from 3 years ago, but without solution

How can I use all function arguments inside javascript Object.assign method

I am making a server using nodejs & express in which user can request some data and server send response. But, the data is array and I want to send a json response to the user. So, I used forEach() method of array and use Object.assign(), so that I can get object. But the problem is I cannot use 'index' argument of forEach() method while 'value' argument is properly getting used inside the callback function. When I use only 'index' argument, then my code runs ok but I want to use both arguments at the same time.
route.get('/getGPX/:number', passport.authenticate('jwt', { session: false }), (req, res) => {
gpx.find({ username: req.user.username }, (err, result) => {
if (err) console.log(err);
if (result) {
if (result.length === 0) {
res.end();
console.log('ended')
}
else {
var jsonRes = {};
result.forEach((value, index) => {
I can use 'value' but not 'index' from the arguments
jsonRes = Object.assign({ index: value.data }, jsonRes);
})
res.json({data: jsonRes});
}
}
})
I even tried using global var, but even it's not working, how can I use index as well as value argument at the same time
What is the JSON structure that you want ?
if you want a json like :
{
0: "my first value",
1: "second"
}
You just miss [] around index, here you put 'index' as a key not the value of index. So you override the index key in each iteration.
Here is the code that use the value of index as a key in a json.
jsonRes = Object.assign({[index]: value.data}, jsonRes)
See here for a working example with more examples : https://repl.it/#Benoit_Vasseur/nodejs-playground
Object.assign mutates the left-most parameter. it does not produce a new object. Since you are passing a literal object every time the jsonRes is going to be the last result.
Put jsonRes in the left instead - Object.assign(jsonRes, {index: value.data})
You might want to use a simple reduce instead of forEach and Object.assign:
} else {
var jsonRes = result.reduce((r, v, i) => {r[i] = v.data; return r}, {});
res.json({data: jsonRes});
}

Return all from type - Freebase MQL

Why does this return a 400 error (Unique query may have at most one result. Got 100):
[{
name:null,
type:'music/artist'
}]
I'd expect it to return the first 100 names from music/artist? How is it a unique query?
I'm trying to build a random query, where I can provide two params - the page and the result from within the page to return - and get one item back
Would be awesome if MQL supported something like return: 'random'
EDIT - adds full Angular factory. The query runs fine if I punch it in to the freebase.com/query engine.
myApp.factory('FB_Random', function ($http) {
return {
fn: function (callback) {
$http.get("https://www.googleapis.com/freebase/v1/mqlread",
{
params: {
query:[{
name: null,
type: '/music/artist'
}]
}
}
).success(function (d, status, headers, config) {
callback(d.result);
}).error(function (d, status, headers, config) {
console.log(d);
});
}
};
});
Looks like there's some undocumented Angular behavior going on here. According to the Angular docs, the params parameter takes a "Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url."
However, it seems like there's some additional logic that looks for array values in the params map and turns them into repeated parameters; ie. ?query={...}&query={...}. Since your query is (properly) wrapped in array notation, indicating that you're expecting multiple results, Angular interprets it as a list with a single value for the query parameter. It then extracts that query parameter and adds it to the url like so:
https://www.googleapis.com/freebase/v1/mqlread?query={%22name%22:null,%22type%22:%22/music/artist%22}
This is why you get the error about your query incorrectly asking for a single result. Because Angular removed the outer array notation that's needed by MQL.
The simplest way to work around this is to simply wrap your query in an additional array like this:
$http.get("https://www.googleapis.com/freebase/v1/mqlread",
{
params: {
query:[[{
name: null,
type: '/music/artist'
}]]
}
}
)
If you'd like to make the fix more explicit and your code easier to read you can just stringify the MQL query yourself like this:
$http.get("https://www.googleapis.com/freebase/v1/mqlread",
{
params: {
query: JSON.stringify([{
name: null,
type: '/music/artist'
}])
}
}
)

Categories

Resources