I am trying to read tags from a selected collection of bibliographic data in ZOTERO with Javascript.
For those who aren't familiar with ZOTERO: it has an in-built "run JS" panel to work directly with items selected / marked in the standalone version.
This is the script I am using to read data from a selected folder and access the tags:
var s = new Zotero.Search();
s.libraryID = ZoteroPane.getSelectedLibraryID();
var itemIDs = await s.search();
for (itemID of itemIDs) {
item = Zotero.Items.get(itemID);
return item;
itemTAG = item.getTags();
return itemTAG;
}
When I call return itemIDs; before the for loop, I get 4943 key:value pairs, which correctly mirrors the number of items in my collection.
The structure looks like this:
[
"0": 21848
"1": 21849
"2": 21850
"3": 21851
"4": 21852
"5": 21853
"6": 21854
"7": 21855
"8": 21856
"9": 21857
"10": 21858
]
What I would actually like to do is iterate through all IDs to get the bibliographic data for each item and return the tags.
This is why I first tried a for/in loop, but this didn't work, supposedly because I wasn't calling the key:value pairs (corresponding to a dictionary in Python?) correctly.
However, the above for/of loop works at least for the first item (item "0") and returns the following data:
{
"key": "BDSIJ5P4",
"version": 1085,
"itemType": "book",
"place": "[Augsburg]",
"publisher": "[Gabriel Bodenehr]",
"date": "[circa 1730]",
"title": "Constantinopel",
"numPages": "1 Karte",
"creators": [
{
"firstName": "Gabriel",
"lastName": "Bodenehr",
"creatorType": "author"
}
],
"tags": [
{
"tag": "Europa"
}
],
"collections": [
"DUW2PJDP"
],
"relations": {
"dc:replaces": [
"http://zotero.org/groups/2289797/items/ZB5J5VZK"
]
},
"dateAdded": "2019-02-13T17:27:29Z",
"dateModified": "2020-03-23T13:13:13Z"
}
So my two questions are:
How can I create a proper for/in loop that retrieves these same data for each item?
How can I return tags only? It seems that item.getTags() [which I used in analogy to the getNotes() examples in the documentation] may not be a valid function. Would that be specific to Zotero or Javascript in general?
Use map() to call a function on every array element and return an array of all the results.
return itemIDs.map(itemID => Zotero.Items.get(itemID).getTags())
Related
The keys and values are separated in the Json object that I get from an api call. I have tried finding a solution It looks like the following:
{
"range": "'1'!A1:AM243",
"majorDimension": "ROWS",
"values":
[
"DeptID",
"DeptDescr",
"VP Area",
"VP Descr",
"HR Category",
"Employee Relations1",
"ER1Title",
"ER1Phone",
"ER1Email",
"Employee Relations2",
"ER2Title",
"ER2Phone",
"ER2Email",
"Compensation1",
"Comp1Title",
"Comp1Phone",
"Comp1Email",
"Compensation2",
"Comp2Title",
"Comp2Phone",
"Comp2Email",
"Employment1",
"E1Title",
"E1Phone",
"E1Email",
"Employment2",
"E2Title",
"E2Phone",
"E2Email",
"Employee Pay Services1",
"EPS1Title",
"EPS1Phone",
"EPS1Email",
"Employee Pay Services2",
"EPS2Title",
"EPS2Phone",
"EPS2Email"
],
[
"20734",
"Academic Success Centers",
"VES",
"VP Enroll Mgmt & Student Aff",
"Administrative",
"Brian Schmidt",
" Employee Relations Consultant",
"(928)523-6139",
"Brian.Schmidt#nau.edu",
"Marcia Warden",
"Assistant Director, Employee Relations",
"(928)523-9624",
"Marcia.Warden#nau.edu",
"Nicole Christian",
"Employment & Compensation Analyst",
"(928)523-6127",
" Nicole.Christian#nau.edu",
"Cathy Speirs",
"Associate Director",
"(928)523-6136",
"Cathy.Speirs#nau.edu",
"Nicole Christian",
"Employment & Compensation Analyst",
"(928)523-6127",
" Nicole.Christian#nau.edu",
"Cathy Speirs",
"Associate Director",
"(928)523-6136",
"Cathy.Speirs#nau.edu",
"Katherine Kurpierz",
"Payroll Specialist",
"(928)523-6129",
"Katherine.Kurpierz#nau.edu",
"Cheryl Brothers",
"Assistant Director - HR Payroll Services",
"(928)523-6085",
"Cheryl.Brothers#nau.edu"
], etc.
But I need it to look like:
[
{
"DeptID": 20734,
"DeptDescr": "Academic Success Centers",
"VP Area": "VES",
"VP Descr": "VP Enroll Mgmt & Student Aff",
"HR Category": "Administrative",
"Employee Relations1": "Brian Schmidt",
"Employee Relations2": "Marcia Warden",
"Compensation1": "Nicole Christian",
"Compensation2": "Cathy Speirs",
"Employment1": "Nicole Christian",
"Employment3": "Cathy Speirs",
"Employee Pay Services1": "Katherine Kurpierz",
"Employee Pay Services2": "Cheryl Brothers"
},etc
I am trying to use the data to populate a drop down using javascript and ajax. Any help is really appreciated.
The object your API returns is not a valid JSON. Was that API made by you or can you get that fixed somehow?
There are 2 things you could do to make it work
-One is change it to return exactly what you want;
-Two is to fix what it returns so that it is a valid JSON;
Going for what is wrong with the file you initially posted, let's remove the contents of the arrays so it's easier to spot the problem:
Your original data looks roughly like this:
{ "range": "'1'!A1:AM243",
"majorDimension": "ROWS",
"values": [],[]
}
To be valid you would need it to look like this:
{ "range": "'1'!A1:AM243",
"majorDimension": "ROWS",
"values": {
"keys": [],
"data": [],
}
}
Notice that I wrapped the two arrays of "values" with { } because it has to be an object if you want it to contain two arrays in it.
Then I gave each array a key with which you can call them. With that you'd be able to get what you want from your "values", so that for each item in the "keys" array you have something in that "data" array.
Hope this helps.
Well let's have a look;
Suppose this is a short version of the response data you got:
var res = `
{
"range": "'1'!A1:AM243",
"majorDimension": "ROWS",
"values": [
"DeptID",
"DeptDescr",
"VP Area"
],
[
"20734",
"Academic Success Centers",
"VES"
],
[
"345543",
"Academic Fails Centers",
"OK"
]
}
`;
As we can see by the first data, looks like a dump from a spreadsheet of sorts, and someone maybe scripted a way to export this data in a JSON-ish way. The values "Array" are the rows of this "spreadsheet".
We will clean it up, and get only the chunks that looks like ["value", "another value", "etc"]
// clean tabs and returns
res = res.replace(/\t/g, '').replace(/\n/g, '');
// get the array-ish chunks
rows = res.match(/\[(((["'])(?:(?=(\\?))\4.)*?\3),*)+\]/gm)
now let's make them real arrays:
var data = rows.map(function (row) {
return JSON.parse(row);
});
Now we have an array of arrays of strings. that means, an array of "rows" that contains the values of the "cells". The first one looks like the header row (the one with the names of the fields)
Lets make objects using each row of data except the first one. The first will serve us as the keys (we match the position (index) of the value from rows[n] from the value on rows[0] to get a key-value pair)
// Here we will define an object to store data
var data_object = { values: [] };
// for each row except the first
for(var i = 1; i < data.length; i++) {
var my_data = {};
//for each element of this row
for(var j = 0; j < data[i].length; j++) {
my_data[data[0][j]] = data[i][j];
}
data_object.values.push(my_data);
}
We have our object, let's suppose you need it in JSON format now:
var json_data = JSON.stringify(data_object);
// let's look what we have here
console.log('json_data:', json_data);
We will look at something like this as a result:
json_data: {"values":[{"DeptID":"20734","DeptDescr":"Academic Success Centers","VP Area":"VES"},{"DeptID":"345543","DeptDescr":"Academic Fails Centers","VP Area":"OK"}]}
NOW A WARNING:
This is what you DON'T want to do if you can fix the API you are getting this data from first. If any inconsistency appears, things will break. and in this example i'm not managing any edge case or exception, neither checking boundaries of arrays or wrapping things in try-catch blocks.
I'm having a rather large amount of difficulty with trying to remove nested objects from my table, without accidentally deleting all my data in the process (happened three times now, thank god I made copies).
My Object:
{
"value1": thing,
"value2": thing,
"value3": thing,
"roles": {
"1": {
"name": "Dave",
"id": "1"
},
"2": {
"name": "Jeff",
"id": "2"
},
"3": {
"name": "Rick",
"id": "3"
},
"4": {
"name": "Red",
"id": "4"
}
}
}`
I've tried a number of rethink queries, but none have worked thus far. It should be noted that 1, 2, 3, & 4 are variables that can have any amount of numbers, and thus my query must reflect that.
Some attempted queries:
function removeRole(id, roleName) {
let role = `${roleName}`
return this.r.table('guilds').get(id).replace(function(s){
return s.without({roles : {[role] : { "name": role }}})
})
}
function removeRole(id, roleName) {
return this.r.table('guilds').getAll(id).filter(this.r.replace(this.r.row.without(roleName))).run()
}
function removeRole(id, roleName) {
return this.r.table('guilds').get(id)('roles')(roleName).delete()
}
Any assistance is greatly appreciated, and if the question has issues, please let me know. Still rather new to this so feedback is appreciated.
I'm not sure if I understood your intention, but the following query seems to do what you're trying to accomplish:
r.db('test')
.table('test')
.get(id)
.replace((doc) => {
// This expression makes sure that we delete the specified keys only
const roleKeys = doc
.getField('roles')
.values()
// Make sure we have a role name is in the names array
.filter(role => r.expr(names).contains(role.getField('name')))
// This is a bit tricky, and I believe I implemented this in a not efficient
// way probably missing a first-class RethinkDB expression that supports
// such a case out of box. Since we are going to delete by nested dynamic
// ids, RethinkDB requires special syntax to denote nested ids:
// {roles: {ID_1: true, ID_2: true}}
// Well, this is just a JavaScript syntax workaround, so we're building
// such an object dynamically using fold.
.fold({}, (acc, role) => acc.merge(r.object(role.getField('id'), true)));
return doc.without({roles: roleKeys});
})
For example, if names is an array, say ['Jeff', 'Rick'], the nested roleKeys expession will be dynamically evaluated into:
{2: true, 3: true}
that is merged into the roles selector, and the above query will transform the document as follows:
{
"value1": ...,
"value2": ...,
"value3": ...,
"roles": {
"1": {"name": "Dave", "id": "1"},
"4": {"name": "Red", "id": "4"}
}
}
I want to create a JSON API that returns a list of objects. Each object has an id, a name and some other information. API is consumed using JavaScript.
The natural options for my JSON output seems to be:
"myList": [
{
"id": 1,
"name": "object1",
"details": {}
},
{
"id": 2,
"name": "object2",
"details": {}
},
{
"id": 3,
"name": "object3",
"details": {}
},
]
Now let's imagine that I use my API to get all the objects but want to first do something with id2 then something else with id1 and id3.
Then I may be interested to be able to directly get the object for a specific id:
"myList": {
"1": {
"name": "object1",
"details": {}
},
"2": {
"name": "object2",
"details": {}
},
"3": {
"name": "object3",
"details": {}
},
}
This second option may be less natural when somewhere else in the code I want to simply loop through all the elements.
Is there a good practice for these use cases when the API is used for both looping through all elements and sometime using specific elements only (without doing a dedicated call for each element)?
In your example you've changed the ID value from 1 to id1. This would make operating on the data a bit annoying, because you have to add and remove id all the time.
If you didn't do that, and you were relying on the sorted order of the object, you may be in for a surprise, depending on JS engine:
var source = JSON.stringify({z: "first", a: "second", 0: "third"});
var parsed = JSON.parse(source);
console.log(Object.keys(parsed));
// ["0", "z", "a"]
My experience is to work with arrays on the transport layer and index the data (i.e. convert array to map) when required.
I want to create a array structure with child entities like this ->
$scope.groups = [
{
"categories": [
{
"name": "PR",
"sortOrder": 0,
"type": "category"
}
],
"name": "DEPT 1",
"sortOrder": 0,
"type": "group",
"id": "-JY_1unVDQ5XKTK87DjN",
"editing": false
}
];
from an array that dosen't have child entities but all the items are listed in one object like this->
$scope.groups = [
{
"name": "PR",
"sortOrder": 0,
"type": "category"
},
{
"name": "AD",
"sortOrder": 3,
"type": "category"
},
{
"name": "DEPT 2",
"sortOrder": 1,
"type": "group",
"id": "-JYZomQKCVseJmaZoIF9",
"editing": false,
"categories": []
},
];
Is there any possible way?
As #Eagle1 has rightly pointed out. You need to define your data model properly to define a function that does that grouping for you. That said, from what I understand you have a $scope.groups array of objects for a specific department containing some categories which you need to consolidate as a child element.
You could start by defining a function that returns an object like you mention:
var organize = function(arr){
cats = [];
dep = {};
$.each( arr, function( i, val ) {
if(val.type == "category")
cats.push(val);
else
dep = val;
});
dep.categories = cats;
return dep;
}
Ultimately, you'll have to traverse the array and look for objects of type category and dump them in an array and have that array as the categories key of the object that you intend to return. I hope it gets you started in the right direction.
of course it is.
It's doable in javascipt although to help you devise something we would need a relationship between categories.
However, that's sounds like something that should be done in your data model (a relationship between dept - category, classic reflexive relationship parent - children). angular should be receiving from the back end an array already ordered.
I'm having trouble finding a solution that will help me loop through a bunch of elements and putting the chosen values into a table. I've been able to withdraw some values but the method isn't dynamic.
Here is an example:
var Table = {
"credit": {
"link": "site link",
"logoUrl": "logo url",
"message": "message"
},
"groups": [
{
"labels": [
{
"name": "Western Conference",
"type": "conference"
},
{
"name": "Central Division",
"type": "division"
}
],
"standings": [
{
"stats": [
{
"name": "gp",
"value": 20
},
{
"name": "w",
"value": 17
},
{
"name": "l",
"value": 0
},
{
"name": "gf",
"value": 64
},
{
"name": "ga",
"value": 37
},
{
"name": "gd",
"value": 27
},
{
"name": "pts",
"value": 37
}
],
"team": {
"id": 12345,
"link": "team link",
"name": "team name",
"shortName": "team"
}
},
This is the structure of the elements. So far I've used this:
document.getElementById("sGamesPlayed").innerHTML=Table.groups[0].standings[0].stats[0].value;
to withdraw values. However there are more teams, stats and divisions so I would need some kind of loop to go through the elements and put the into a dynamic table.
I would consider you to look at http://underscorejs.org/.
it provides a bunch of utility functions that could help you,
for example, _.each() helps you loop through JSON properties.
for the sample objects you've given (after completing the missing brackets at the end),
_.each(Table.groups[0].standings[0].stats, function(stats){
console.log(stats['name']+","+stats['value'])
})
gives me:
gp,20
w,17
l,0
gf,64
ga,37
gd,27
pts,37
how it works is that you provide the object you want as the first argument and the function that you give as the second argument will be called with each element of the first argument (Assuming it is a list).
I would also urge you to look at underscore templating that you can use to render your table where i put the console.log :
http://net.tutsplus.com/tutorials/javascript-ajax/getting-cozy-with-underscore-js/
http://scriptble.com/2011/01/28/underscore-js-templates/
I guess your question is about filtering the values of the array standings. In order to do that you can use the jQuery grep function (if you want to use jQuery).
For example you can write:
var arr = $.grep(Table.groups[0].standings[0].stats, function(d){return d.value>25})
Which will give
arr = [{"name": "gf","value": 64}, {"name": "ga", "value": 37},{"name": "gd", "value": 27},{"name": "pts", "value": 37}]
If this is not what you meant, can you please create a jsFiddle with a sample of what you want?
Depending on what you want to do with the results, you can go over the object using a scheme like:
var groups, standings, stats, value;
groups = Table.groups;
// Do stuff with groups
for (var i=0, iLen=groups.length; i<iLen; i++) {
standings = groups[i].standings;
// Do stuff with standings
for (var j=0, jLen=standings.length; j<jLen; j++) {
stats = standings[j];
// Do stuff with stats
for (var k=0, kLen=stats.length; k<kLen; k++) {
value = stats[k].value;
// Do stuff with value
}
}
}
Of course I have no idea what the data is for, what the overall structure is or how you want to present it. But if you have deeply nested data, all you can do is dig into it. You might be able to write a recursive function, but it might also become very difficult to maintain if the data structure is complex.