JavaScript / JSON - Access array element using a key name as index - javascript

I want to be able to access an array element by using a unique key name instead of a numeric index. In my situation, I'm making a Discord bot and each servers have their own settings. When someone sends a message on a server, I want to access some of this server's settings (such as a message prefix). IMPORTANT: Right now, the only way I can do this is looping through all the servers that the bot is in, which in long-term, could slow it down if there's hundreds of active servers sending messages. So looping through all the servers is already being done right now, but I want a direct way without having to do this.
conf.json:
{
"Settings": [
"358262452343013386" {
"prefix": "$",
"Admins": [
"155444308395294720"
],
"NotificationChannel": "358772856282284033",
"robotpieces": []
}
]
}
What I want to be able to do in my bot.js:
console.log(conf.Settings[message.guild.id].prefix); // outputs the prefix
// message.guild.id is the id of the server, which in this case, would translate to this:
console.log(conf.Settings["358262452343013386"].prefix) // outputs '$'
Any ideas of how I can achieve a similar goal WITHOUT having to loop through all of the array?
EDIT:
I know the following JSON is invalid, but I want a solution which would give the same result.

Aside from the fact that the JSON you posted is invalid, you could store the server settings as an object rather than an array, and access is just like you are trying to:
{
"Settings": {
"358262452343013386": {
"prefix": "$",
"Admins": [
"155444308395294720"
],
"NotificationChannel": "358772856282284033",
"robotpieces": []
}
}
}

Related

Logic App bypass Null in filter query

I am building a Logic App that uses the Azure Resource connector to obtain a list of my resources. I would then like to filter the results to Microsoft.Compute resources that have a tag name and value of stop and normal.
Here is a snippet of resource that I receive back without any filters
{
"id": "/subscriptions/<subscription>/resourceGroups/Env1/providers/Microsoft.Compute/virtualMachines/MyVM1",
"name": "MyVM1",
"type": "Microsoft.Compute/virtualMachines",
"location": "westeurope",
"tags": {
"stop": "normal"
}
},
{
"id": "/subscriptions/<subscription>/resourceGroups/LogicApp/providers/Microsoft.Logic/workflows/DWSize-Check",
"name": "DWSize-Check",
"type": "Microsoft.Logic/workflows",
"location": "westeurope",
"tags": {}
}
As you can see, the bottom resource does not contain any tags, as with many others that will appear in the list
I use the standard Compose Filter Array connector to try and filter out from the value I receive back.
Here is the code that I wish to use for the filter command:
#and(contains(item()?['id'], '/Microsoft.Compute/virtualMachines/'), contains(item()?['tags'], variables('TagName')), contains(item()?['tags'], variables('TagValue')))
variables('TagName') and variables('TagValue') will be stop and normal, as show in the example tags listed in my results snippet.
However, because there is no tag values listed in other resources, such as Microsoft.Logic/workflows, I receive the following null error:
InvalidTemplate. The execution of template action 'Filter_array'
failed: The evaluation of 'query' action 'where' expression
'#contains(item()?['tags'], variables('TagValue'))' failed: 'The
template language function 'contains' expects its first argument
'collection' to be a dictionary (object), an array or a string. The
provided value is of type 'Null'.'.
Would anyone know how to get around this?
I have tried similar queries to this #contains(item().tags?.stop, variables('TagValue')) just to see if it picks up anything, but I'm still blocked by a null response :(
I tried the above with the help of the Workflow Definition Language, but still no dice. Any help would be greatly appreciated.
EDIT
In addition to the answer posted by Thomas, I have performed the following (image below) to filter out null from the results and get to the TagName, but I am still unable to get to the TagValue, even if I use contains:
#and(contains(item()?['tags'], variables('TagName')), contains(item()?['tags'], variables('TagValue')))
or event just trying to look for TagValue
#contains(item()?['tags'], variables('TagValue'))
You can check for null and return an empty value (an empty array in your case).
You can replace item()?['tags'] with this expression or create a variable :
if(equals(item()?['tags'], null), [], item()?['tags'])

Fetch list of 50,000 most subscribed channels

I'm trying to figure out a way to grab the top 50,000 most subscribed youtube channels using javascript. These only need to be grabbed once and will be stored in a file to be used for an autocomplete input in a webpage.
I've gotten pretty close to getting the first top 50 by using search:list (/youtube/v3/search) by searching with parameters maxResults=50, order=viewCount, part=snippet, type=channel, fields=nextPageToken,items(snippet(channelId,title))
Returning:
{
"nextPageToken": "CDIQAA",
"items": [{
"snippet": {
"channelId": "UC-9-kyTW8ZkZNDHQJ6FgpwQ",
"title": "Music"
}
},{
"snippet": {
"channelId": "UC-lHJZR3Gqxm24_Vd_AJ5Yw",
"title": "PewDiePie"
}
},{
"snippet": {
"channelId": "UCVPYbobPRzz0SjinWekjUBw",
"title": "Анатолий Шарий"
}
},{
"snippet": {
"channelId": "UCam8T03EOFBsNdR0thrFHdQ",
"title": "VEGETTA777"
}
},...
Then all I'd have to do is fetch that 1000 more times using the nextPageToken to get a list of the top 50,000.
Unfortunately, sorting by relevance, rating, viewCount, or nothing is not yielding the 50 most subscribed channels, and there doesn't seem to be any sort of way to order them by subscriber count according to the documentation; so it seems like i am stuck.
Just before you writing your 50 results in file (or database), you can make one more API call, using channelId field from your result, and merge all of them with comma delimited and make another API call Channels: list.
On that page for example you can use following parameters:
(these are IDs from your example above)
part=statistics
id=UC-9-kyTW8ZkZNDHQJ6FgpwQ,UC-lHJZR3Gqxm24_Vd_AJ5Yw,UCVPYbobPRzz0SjinWekjUBw,UCam8T03EOFBsNdR0thrFHdQ`
And result will look something like this:
{
"kind": "youtube#channel",
"etag": "\"m2yskBQFythfE4irbTIeOgYYfBU/MG6zgnd09mqb3nAdyRnPDgFwfkE\"",
"id": "UC-lHJZR3Gqxm24_Vd_AJ5Yw",
"statistics": {
"viewCount": "15194203723",
"commentCount": "289181",
"subscriberCount": "54913094",
"hiddenSubscriberCount": false,
"videoCount": "3175"
}
}
And you can take subscriberCount from result for each channel.
I know, this is not the way to sort your 50 results while writing into the file,
but with this you can sort later your results by "subscriber count" while fetching from file for your autocomplete input.
I didn't find any other way to sort results by subscriber count, so maybe this can be helpful.
The idea to do is to run a server side script, that makes RESTful api calls in a loop, and writes the results to .JSON file, to save results. For that you can create PHP script, that makes REST API call to google, and fetch first 50 results, and then use file write operations to write your results. Run that PHP script as corn job to update results at regular intervals. Executing corn job at every specific time interval you set keeps results fresh.
Hit CURL command with loop for next, to fetches 50 results every time and create temp file with all the results saved in .JSON file. Once your results are fetched, replace your old JSON file with newly created temporary file. This will generate fresh JSON file are regular, with new results if any changes are made to data.
However, the idea to use temporary file is to avoid script avoid wait/slow of AJAX down due to consistent read and write operations on same file. Once temporary file is written, simply use move command to replace the actual file.
Make sure, you use cache control headers in AJAX results to keep its freshness of data.

Detect when no previous posts available in Facebook Graph posts edge?

I'm accessing the Facebook Graph API for posts and am trying to figure out the pagination handling. I understand the use of paging.next and paging.previous properties of the results but I'd like to know when there are actually previous results. Particularly, when I make the first 'posts' call, I get back a paging.previous url even though there are no previous values. Upon calling that url I get a response with no results.
For example, calling "168073773388372/posts?limit=2" returns the following:
{
"data": [
{
"story": "Verticalmotion test added a new photo.",
"created_time": "2015-12-02T17:04:56+0000",
"id": "168073773388372_442952469233833"
},
{
"message": "http://www.youtube.com/watch?v=QD2Rdeo8vuE",
"created_time": "2013-12-16T23:19:30+0000",
"id": "168073773388372_184840215045061"
}
],
"paging": {
"previous": "https://graph.facebook.com/v2.6/168073773388372/posts?limit=2&format=json&since=1449075896&access_token=****&__paging_token=enc_AdA69SApv4VoBZB0PPZA7W5EivCYQal8KMFmRNkyhr8ZBk4w0YmFEQUJWV3JZBS70ihyMpbqieQaERhY50enqNCMBuIZATadeopYj8xPvQL7Y8KueaQZDZD&__previous=1",
"next": "https://graph.facebook.com/v2.6/168073773388372/posts?limit=2&format=json&access_token=****&until=1387235970&__paging_token=enc_AdAVMaUlPmpxjBmq5ZClVdNpFp7f9MyMFWjE7ygqsMLW7zvSx3eGHLkfwDxdCx0uO3ooAZCKDmCwMWHZA9RNyxkYUPJyjMtO3kynKm5uF2PhoPZB2gZDZD"
}
}
How can I tell if it's the first set of results?
From tidbits scattered around the documentation and web, it seems like the previous url shouldn't be there.
I don't think it matters because I get the same results in the Graph Explorer but I'm using OpenFB to access the API.
You can set the order to be reverse then get the 1st result
https://developers.facebook.com/docs/graph-api/using-graph-api
Ordering
You can order certain data sets chronologically. For example you may sort a photo's comments in reverse chronological order using the key reverse_chronological:
GET graph.facebook.com
/{photo-id}?
fields=comments.order(reverse_chronological)
order must be one of the following values:
*chronological*
*reverse_chronological*

JavaScript JSON Parse Issue ($.getJSON)

I can't seem to get the following code to output anything to the screen. JavaScript is not my strong suit!
This is the JavaScript code calling a local file that outputs a list of rooms
$.getJSON('roomupdate.php?level=1&div=1&callback=json',function(res){
console.log(res.rooms.name[0]);
});
In the above I'm merely trying to see the name of the first room in the console. And this is the JSON output (Which I can confirm the script can see and load)
json(
{
"rooms":
[
{ "name" : "Bedroom" },
{ "name" : "Living Room" },
{ "name" : "Lounge" },
{ "name" : "Kitchen" }
]
})
Could anyone suggest what I am doing wrong? Even to view in the console?
Lastly, can you loop through the array?
Your JSON data contains an object rooms and this object actually contains an array [], So to access the data inside your array you need to put the index on rooms :
console.log(res.rooms[0].name);
Use callback=? rather than callback=json so that jQuery knows you are using JSONp and can choose it's own name for the callback function.
$.getJSON('roomupdate.php?level=1&div=1&callback=?',function(res){
//alert('Your name is '+res.rooms);
console.log(res.rooms.name[0]);
});
See http://api.jquery.com/jQuery.getJSON/#jsonp for details.
Edit:
Looking again, you will need to change the way you are accessing the data around. res.rooms.name[0] should be res.rooms[0].name because rooms is a list, and each room has a name property.
This will loop through the array of rooms and log the name of each one:
$.each( res.rooms, function( i, room ) {
console.log( room.name );
});
If that doesn't work, then add this statement at the beginning of your callback (where you have the console.log() call now):
debugger;
Load/run your page with the developer tools open, and it will stop in the debugger where you have that statement. Now you can look at all your variables in detail, try out expressions to see what they do, single step through your code, etc.

URL GET parameter representing and parsing embedded objects

I'm working on an API that consists of several collections that have relations to each other. I want to give users the opportunity to eagerly fetch records from associated collections via a GET url parameter.
For instances
/api/clients/
Would return an array of objects, each representing a client.
But clients have "employees" and "templates". "Templates" also have "revisions." And lastly, "revisions" have "groups"
My strategy for the format of the url parameter is something like this:
/api/clients?expand=[employees][templates[revisions[groups]]]
Which represents:
clients
+ employees
+ templates
+ revisions
+ groups
My question is, what is a good way to go about parsing a string in this format: [employees][templates[revisions[groups]]]
to arrive at an object like this:
{
"employees": {},
"templates": {
"revisions": {
"groups": {}
}
}
}
Or something similar that's easy to work with. I'm working in NodeJS so any answers specific to that environment are a plus. Regex?
Going off #Kovge 's suggestion, I'm going to handle this situation with a JSON string passed in my URL get request. It's a bit verbose, but should be acceptable.
Here's how I'd represent my eager-fetching associated collection records:
[
{
"resource": "employees"
},
{
"resource": "templates",
"extend": [
{
"resource": "revisions",
"extend": [
{
"resource": "groups"
}
]
}
]
}
]
Basically using arrays of objects with "resource" parameters and optional "extend" parameters. My request will end up looking like this: (url encoded)
http://example.com/api/clients?extend=%5B%7B%22resource%22%3A%22employees%22%7D%2C%7B%22resource%22%3A%22templates%22%2C%22extend%22%3A%5B%7B%22resource%22%3A%22revisions%22%2C%22extend%22%3A%5B%7B%22resource%22%3A%22groups%22%7D%5D%7D%5D%7D%5D
EDIT:
This is what my result ended up looking like, still playing with things.

Categories

Resources