return value as key on map reduce - javascript

Currently my views on couchdb always return something like this:
{
"key":"somekey",
"value":"somevalue"
}
I wonder if there's any way to return something like this:
{
"somekey":"somevalue"
}
i need to access "somekey" directly without having to search the whole array for a key with "somekey" value on it.
Thanks

When using a view, there is no direct way provided by CouchDB to return the collection of documents as anything but key/value as you've seen.
You'd need to map the data on the client side into a keyed index, or request the document by the key directly from the view:
/sample/_design/docs/_view/by_somekey?key="somekey"
If you're looking for a fast map option in JavaScript (avoiding calling a function for each iteration), you could just use this simple logic of course:
// docs are the documents returned from the view
var map={};
for(var i=0,len=docs.length;i<len;i++) {
map[docs[i].key] = docs[i].value;
}

You can do this with underscoreJS (http://underscorejs.org/#reduce).
var map = {
"key":"somekey",
"value":"somevalue"
};
var result = _.reduce(map, function(key, value){
var result = {};
result[key] = value
return result;
});
alert(result.somekey);
http://jsfiddle.net/BcYtw/1/

If you do not like the format or data for Couch queries, you can reformat it at your will with list function. Something like this should work for you:
function(head, req){
send('[');
row = getRow();
if (row){
// here you make your own JS structure to be returned,
// serialize it with toJSON() and send to client with send()
// First row:
var new_row = {};
new_row[row.key] = row.value;
send(toJSON(new_row));
while(row = getRow()){
// Next rows:
send(',');
var new_row = {};
new_row[row.key] = row.value;
send(toJSON(new_row));
}
}
return ']';
}

Related

Getting array values from multidimensional array in JavaScript

I'm in need of some minor assistance. I'm having trouble getting an array (larray3) populated with two other array objects (larray1 and larray2) to pass both from data.js into the subsequent model.js and view.js. Data.js correctly builds the multidimensional array however when the results are received in model.js/view.js I only receive the results for larray1. Because only the first values come thru I cannot tell if both larray1 and larray2 are actually passing thru. Can someone please tell me how I should alter my syntax in either model.js or view.js to access both array values or what else I could change? Thank you in advance.
data.js.
function getCountries(done) {
var sqlite3 = require('sqlite3').verbose();
var file = 'db/locations.sqlite3';
var db = new sqlite3.Database(file);
var larray1 = [];
var larray2 = [];
var larray3 = [];
db.all('SELECT * FROM Country', function(err, rows) {
// This code only gets called when the database returns with a response.
rows.forEach(function(row) {
larray1.push(row.CountryName);
larray2.push(row.CountryCode);
})
larray3.push(larray1);
larray3.push(larray2);
return done(larray3[0], larray3[1]);
});
db.close();
}
model.js
function Countries(done) {
//Pull location values from data
return getCountries(done);
}
view.js
function viewCountries() {
var viewCou = Countries(function(results) {
// Code only gets triggered when Countries() calls return done(...);
var container = document.getElementById('country-select');
var fragment = document.createDocumentFragment();
results.forEach(function(loc, index) {
var opt = document.createElement('option');
opt.innerHTML = loc;
opt.value = loc;
fragment.appendChild(opt);
});
container.appendChild(fragment);
})
}
In data.js you send two arguments to the done callback:
return done(larray3[0], larray3[1]);
This done function is passed through in your model.js:
return getCountries(done);
And that done is passed in from view.js:
Countries(function(results) { // ...
So it is this anonymous function (function(results) {...}) that is called in data.js. But notice that this function only has one parameter, so you're doing nothing with the second argument that data.js sends. result gets the value of larray3[0], but larray3[1] is not captured anywhere.
You could solve this in different ways. Personally, I think the design with two arrays is wrong from the start. I would not separate data that belongs in pairs (name and code) into two different arrays.
Instead make an array of objects, and pass that single array around:
In data.js:
rows.forEach(function(row) {
larray1.push({
name: row.CountryName,
code: row.CountryCode
});
})
return done(larray1);
In view.js:
opt.textContent = loc.name;
opt.value = loc.code;
Side-note: .textContent is preferred over .innerHTML when assigning plain text.

RSVP.js - Multiple asynchronous function calls on array

I have a result from a REST call that contains a list of files. Each file has properties that I have to extract and place in a new array. This is straightforward and is easily done with a simple loop. Three of the properties I need to extract are accessible in a direct way, while three other properties are of HATEOAS type, which in other words mean that for each file in the result, I have to make three other asynchronous calls to retrieve it's values.
My first instinct was to use RSVP.all() to process my promises and to map the items in the new array to the corresponding properties in the original list of files using map, but I cannot figure out how to achieve this.
I want to achieve something like below, but I have no idea how I can get the index of the current mapped item in itemList, to include the correct file from fileList. How can I do this?
On a sidenote, if I use RSVP.all() the wrong way I'm happy to receive tips!
function createItemList(fileList) {
var promise = new RSVP.Promise(function(resolve, reject) {
var itemList = [];
//For each file in fileList, get the directly accessible properties
//and push it to a new array
for (var i = 0, file; file = fileList[i]; i++) {
currentItem.Name = file.Name;
currentItem.LastModified = new Date(file.TimeLastModified).format("dd-MM-yyyy hh:mm:ss");
currentItem.Version = file.MajorVersion + "." + file.MinorVersion;
itemList.push(currentItem);
}
//This is where it starts to get messy...
//I want to map each item in the new itemlist to the corresponding properties
//in the fileList. If I can include the corresponding file somehow, I could set the
//data in the method 'getModifiedBy' and similar. I believe this should work,
//but I have no idea how I can achieve this.
var modifiedBy = itemList.map(function(item) {
return getModifiedBy(item, fileList[INDEX]);
});
var checkedOutBy = itemList.map(function (item) {
return getCheckedOutBy(item, fileList[INDEX]);
});
var eventDate = itemList.map(function (item) {
return getEventDate(item, fileList[INDEX]);
});
var promises = {
promisesModifiedBy: modifiedBy,
promisesCheckedOutBy: checkedOutBy,
promisesEventDate: eventDate
};
RSVP.all(promises)
.then(function() {
resolve(itemList);
});
});
return promise;
}
Use only a single map over the itemlist that returns a Promise for your 3-property-object. Use a dedicated helper function for single items.
Btw, with new RSVP.Promise you're using the deferred antipattern while there's absolutely no reason - RSVP.all() already returns you the result promise.
function createItemList(fileList) {
return RSVP.all(fileList.map(createItem));
}
function createItem(file) {
// get the directly accessible properties
var currentItem = {};
currentItem.Name = file.Name;
currentItem.LastModified = new Date(file.TimeLastModified).format("dd-MM-yyyy hh:mm:ss");
currentItem.Version = file.MajorVersion + "." + file.MinorVersion;
return RSVP.hash({
modifiedBy: getModifiedBy(currentItem, file),
checkedOutBy: getCheckedOutBy(currentItem, file)
eventDate: getEventDate(currentItem, file)
}).then(function(asyncprops) {
// somehow mix the asyncprops with the currentItem
return item; // into a result item
});
}

Properly storing an object in chrome.storage?

I'm trying to store an object in my chrome extension containing other objects using chrome.storage - but am having trouble initializing it and properly fetching it using chrome.storage.sync.get. I understand that I'm supposed to get objects in the form of chrome.storage.sync.get(key: "value", function(obj) {} - the issue is I'm not sure how to
Initialize the object the first time with get
Properly update my object with set
I have the following code to create the object and add the data I need.
allData = {};
currentData = {some: "data", goes: "here"};
allData[Object.keys(allData).length] = currentData;
This will correctly give me an object with it's first key (0) set to currentData. (Object: {0: {some: "data", goes: "here"}}) Working as intended, and allData[Object.keys(allData).length] = currentData; will properly push whatever currentData is at the time into my Object later on.
But how do I properly store this permanently in chrome.storage? chrome.storage.sync.get("allData", function(datas) {}) fails to create an empty allData variable, as does allData: {}, allData = {}, and a variety of different things that return either undefined or another error. How do I properly initialize an empty object and store it in chrome.storage? Or am I going about this all wrong and need to break it down into associative arrays in order for it to work?
I essentially need that small block of working code above to be stored permanently with chrome.storage so I can work with it as needed.
You first need to set the data inside the storage:
allData = {};
currentData = {some: "data", goes: "here"};
// to initialize the all data using the storage
chrome.storage.sync.get('allData', function(data) {
// check if data exists.
if (data) {
allData = data;
} else {
allData[Object.keys(allData).length] = currentData;
}
});
// Save it using the Chrome extension storage API.
chrome.storage.sync.set({'allData': allData}, function() {
// Notify that we saved.
message('Settings saved');
});
After that you should be able to access the data using the chrome.storage.sync.get('allData', function(){ ... }) interface.
You can easily do this with the new JavaScript (ECMAScript 6), have a look into Enhanced Object Properties:
var currentData = {some: "data", goes: "here"};
chrome.storage.local.set({allData: currentData });
In the old way, was something like this:
var obj = {};
var key = "auth";
obj[key] += "auth";
obj[key] = JSON.stringify({someKey: someValue});

Ember.js how to concat two objects

I have an object in Ember (let's call it existing):
var existing = {
items: [
...
],
...
}
On the callback of hitting a server side request, there is a new response called result which looks the same as existing. I need to take the items in existing, and prepend them to the new result. So I have the following:
var result = { ... };
var existing = this.get('content');
result.items = result.items.concat(existing.items);
this.set('content', result);
The problem is, when rendered in the template, it is just displaying the new items from result, the old items from existing are not being displayed even though are in items. Any ideas why?
Thanks.
I guess the problem is that using concat which is not supported in Ember.Array (http://emberjs.com/api/classes/Ember.Array.html) does not trigger bindings and therefore your view is not updated. To get the items arrays merged you could do something like:
var result = { ... };
var existing = this.get('content');
result.items.forEach(item) {
existing.items.pushObject(item);
}
this.set('content', existing);
Hope it helps
If result.items is an array and you want to use the javascript concat method you can call toArray on the existing.items enumerable.
var result = { ... };
var existing = this.get('content');
result.items = result.items.concat(existing.get('items').toArray());
this.set('content', result);
If result.items is already an Ember.Enumerable I would use pushObjects or addObjects. http://emberjs.com/api/classes/Ember.MutableArray.html#method_addObjects
var result = { ... };
var existing = this.get('content.items');
result.items = result.get('items').addObjects(existing);
this.set('content', result);
I see this is an old post, but since there isn't an accepted answer, I thought I'd throw in what I feel is the best option. I recently found myself needing to concat a bunch of DS.ManyArray together and this was the solution I came up w/:
var myArrayOfHasManyArrays = ...;
var concatenated = [].concat.apply([], myArrayOfHasManyArrays.invoke('toArray'));
Hope this helps!

I want get one specific entry from JSON data array and do some modification

first I got JSON data via web server just like
$.getJSON(url,function(){
//my callback function;
});
And now I've got data as following:
{entries:[{title:'foo',id:'UUID',finished:null},{title:'bar',id:'UUID',finished:null},{title:'baz',id:'UUID',finished:null}]}
I have to find one specific JSON entry by it's UUID, and after that I need to modify one part for example, make a new json data:
{title:'foo',id:'UUID',finished:true}
And send back to server by using
$.post(url, data);
I'm totally lost myself with this situation... can anyone help?
Assuming that you've put the data in a variable called result, like this:
var result = {entries:[{title:'foo',id:'UUID',finished:null},{title:'bar',id:'UUID',finished:null},{title:'baz',id:'UUID',finished:null}]}
You could do a for-loop:
for ( var i=0; i<result.entries.length; i++ ) {
if (result.entries[i].id == 'the_UUID_you_are_looking_for') {
var entry = result.entries[i]; // "entry" is now the entry you were looking for
// ... do something useful with "entry" here...
}
}
Edit - I've written the full solution below, to further illustrate the idea and avoid misunderstandings:
// Get data from the server
$.getJSON("url", function(result) {
// Loop through the data returned by the server to find the UUId of interest
for ( var i=0; i<result.entries.length; i++ ) {
if (result.entries[i].id == 'the_UUID_you_are_looking_for') {
var entry = result.entries[i];
// Modifiy the entry as you wish here.
// The question only mentioned setting "finished" to true, so that's
// what I'm doing, but you can change it in any way you want to.
entry.finished = true;
// Post the modified data back to the server and break the loop
$.post("url", result);
break;
}
}
}
Try this:
var modified = false, myUuid = 'some uuid';
for (i = 0; i < data.entries.length; i++) {
if (data.entries[i].id === myUuid) {
data.entries[i].finished = true;
modified = true;
break;
}
}
if (modified) {
$.post(url, data);
}
You need to loop through your data. Alternatively you could restructure your JSON:
{"entries":{"UUID1":{"title":"foo", "finished": false }}, {"UUID2":{"title":"bar", "finished":false}}}

Categories