Data pointers? Shifting one array seems to affect another - javascript

When I shift the tempArr, async.eachSeries will actually skip coordinates (or it seems like coordinatesArr is getting shifted as well). I think it has to do with the pointers pointing to objects with the same memory location, but I have no concept of how that works. What is actually happening when I assign values to variables?
The code will work if I do
tempArr = _.toArray(coordinates);
instead of
tempArr = coordinatesArr;
Sample below:
var coordinatesArr, tempArr;
var marginLon = [];
var marginLat = [];
var coordinates = {
'0': { lon: 13.18472, lat: 32.88972 },
'1': { lon: 13.400454, lat: 32.767144 },
'2': { lon: -120.59234, lat: 47.372269 },
'3': { lon: 122.065977, lat: -26.471618 },
'4': { lon: 122, lat: -25 }
}
coordinatesArr = _.toArray(coordinates);
tempArr = coordinatesArr;
// Will work if changed to below
// tempArr = _.toArray(coordinates);
async.eachSeries(coordinatesArr, function(set, acb) {
tempArr.shift();
if(tempArr.length < 1) return acb();
async.eachSeries(tempArr, function(set2, aacb) {
marginLon.push(Math.abs(set.lon - set2.lon));
marginLat.push(Math.abs(set.lat - set2.lat));
aacb();
}, function(err) {
if(err) return acb();
acb();
});
}, function(err) {
if(err) return;
return;
});
Thank you

All variables can ultimately only contain scalar (non-compound) values. However some of those values are simply references objects (e.g. pointers to object or array). You must recognize when you need a copy of the data that you can modify while retaining the original.
first question why don't you start with an array?
var coordinatesArr = [
{ lon: 13.18472, lat: 32.88972 },
{ lon: 13.400454, lat: 32.767144 } // etcetera
]
secondly, shift is a destructive operation, if tempArr and coordinatesArr both point to the same physical array, modifying one modifies both.
You need something like the line below to create a modifiable array that doesn't harm the orginal:
var tempArr = coordinatesArr.slice();

Related

console.log of array returns undefined

I am trying to take two pieces of data from an object and push it as a new object into an array. The data is being supplied by an API call to my SQL database. That API call is working correctly and displaying the object in a console table. When the script runs a forEach method to extract the data into its own object and then push that new object to a new array, the new array returns "undefined". Code below:
Example data (only one placeholder entry currently, the events array will be seeded with multiple examples in this format)
events = [{location: "Emergency Shelter", latitude: "37.5434", longitude: "-77.4435"}]
Empty arrays declared and API call functioning properly:
let events = [];
let locations = [];
$.get("/api/events", data => {
for (let i = 0; i < data.length; i++) {
events.push(data[i]);
}
});
console.table displays the object correctly and includes the keys "latitude" and "longitude" with the correct corresponding values
forEach method:
locations = events.forEach(location => {
const coords = {};
coords.latitude = location.latitude;
coords.longitude = location.longitude;
locations.push(coords);
});
console.log("Coordinates list: " + locations);
console.log displays "Coordinates list: undefined"
I feel like I may be missing a return somewhere, but I'm not sure where. I tried adding
return locations;
inside the forEach method but it doesn't change anything (and I believe that would exit my function prior to getting through the entire array). Any help is appreciated!
forEach returns nothing so locations should be undefined. You shouldn't pass return value of forEach to locations
events.forEach(location => {
const coords = {};
coords.latitude = location.latitude;
coords.longitude = location.longitude;
locations.push(coords);
});
console.log("Coordinates list: " + locations);
Also you can use map function.
const events = [
{ location: 'Emergency Shelter', latitude: '37.5434', longitude: '-77.4435' }
];
const locations = events.map(({ latitude, longitude }) => ({
latitude,
longitude
}));
console.log(locations);
Try a map based approach which also would condense your code to ...
const events = [{
location: "Emergency Shelter",
latitude: "37.5434",
longitude: "-77.4435"
}, {
location: "Peopl's Kitchen",
latitude: "36",
longitude: "-78"
}, {
location: "Salvation Army",
latitude: "38",
longitude: "-76"
}];
const locations = events.map(({ latitude, longitude }) => ({ latitude, longitude }));
console.log("Coordinates list: ", locations); // do not concatenate the result.
console.log("Coordinates list: " + locations);
console.log("Coordinates list: " + JSON.stringify(locations));
.as-console-wrapper { min-height: 100%!important; top: 0; }
map creates a new array by iterating another one where each item of the new array equals the return value of the mapping/transforming function which, at each iteration step, does process the current value of the original array.

How to populate array with multiple data types inside?

I want to populate "markers" array that contains object and string. I have another javascript object stored in obj variable and I want to extract data from it and store it into markers array.
I am populating array with following code
for (i = 0; i < obj.results.bindings.length; i++) {
markers[i].content = obj.results.bindings[i].placeName.value;
markers[i].coords.lat = Number(obj.results.bindings[i].lat.value);
markers[i].coords.lng = Number(obj.results.bindings[i].long.value);
}
and markers array looks like this (populated manually, not by for loop)
var markers = [
{
coords:{lat:37.8707,lng:32.5050},
content:'<h1>Mevlana museum</h1>'
},
{
coords:{lat:37.8699,lng:32.4993},
content:'<h1>Kapu Camii</h1>'
},
{
coords:{lat:37.8749,lng:32.4931},
content:'<h1>Karatay Madrasa</h1>'
},
{
coords:{lat:37.8749,lng:32.4931},
content:'<h1>Karatay Madrasa</h1>'
},
{
coords:{lat:37.8749,lng:32.4931},
content:'<h1>Karatay Madrasa</h1>'
}
];
Now when javascript object 'obj' have same number of nested objects as elements of markers it works fine but when 'obj' have more nested objects than markers array has elements problems. I just want to add rest of the nested objects from 'obj' into markers. Why doesn't markers array grow dinamically when I add new elements in the for loop?
How about this?
Array should not grow dynamically. You could use push/pop method.
markers = [];
for (i = 0; i < obj.results.bindings.length; i++) {
markers.push({
content : obj.results.bindings[i].placeName.value,
coords : {
lat : Number(obj.results.bindings[i].lat.value),
lng : Number(obj.results.bindings[i].long.value)
}
})
}
I try again with your code below:
for (i = 0; i < obj.results.bindings.length; i++) {
markers[i] = {};
markers[i].content = obj.results.bindings[i].placeName.value;
markers[i].coords= {};
markers[i].coords.lat = Number(obj.results.bindings[i].lat.value);
markers[i].coords.lng = Number(obj.results.bindings[i].long.value);
}
I guess that you didn't assign the type as object, so it went wrong.
Please let me know if this works too.
You can use forEach of your obj.results.bindings then construct each and every markers that you want and eventually push into the array as below
let obj = {
results: {
bindings: [
{ placeName: 'cali', lat: '123', long: '456' },
{ placeName: 'hawaii', lat: '555', long: '333'},
{ placeName: 'korea', lat: '777', long: '888' }]
}
};
let markers = [];
obj.results.bindings.forEach(item => {
let mar = { content: '', coords: {}};
mar.content = item.placeName;
mar.coords.lat = Number(item.lat);
mar.coords.lng = Number(item.long);
markers.push(mar)
})
console.log(markers)

How to declare a Hash/Dictionary of Array

I have a program that pushes values into one data structure like this:
if(symbolType == "C" || symbolType == "P") // The calls and puts
stocks.push({
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
});
}
else // The stock
{
stocks.push({
symbol: symbol,
open: 0,
type: symbolType
});
}
So this is the key: NOT A STRING!
{
symbol: symbol,
open: 0,
type: symbolType
}
And the values of which are many look like this:
{
symbol: symbol,
undsymbol: undSymbol,
open: 0,
type: symbolType,
expiry: expiry,
days: days,
strike: strike
}
The problem is that stocks and calls and puts are being put into one collection. Instead, I want to add the the stocks and their corresponding calls and puts into a dictionary/map, where the stocks are the keys, and the calls and puts get pushed into an array indexed by it's stock.
At the end, I want to be able to iterate and get the keys and values.
How do I declare this object
Index into it to see if the key[stock] already exists, if it doesn't add it with an empty array.
If I get a "C" or "P", I want to get the corresponding array that holds the Calls/Puts for this key [stock] and push the call/put into the array.
Initially I thought the declaration was something like this:
var stockCallsPutDict = {[]}
stockCallsPutDict[stock] = [];
stockCallsPut[stock].push(call);
// Pretty print the dict of keys and its options =
stockCallsPutDict.forEach(function kvp) {
...
}
If ES6 is an option, you can either build an object yourself or use a Map.
Here's some quick code I came up with:
const stocks = {};
const addCallAndPut = callAndPut => {
const symbol = callAndPut.symbol;
if (!stocks[symbol]) {
stocks[symbol] = [];
}
stocks[symbol].push(callAndPut);
}
const showStuff = () => {
for (const symbol in stocks) {
// output stuff using stocks[symbol]
}
}
OR WITH A MAP
const stocks = new Map();
// basic implementation
const addCallAndPut = callAndPut => {
const stockCallsAndPuts = stocks.get(callAndPut.symbol) || [];
stockCallsAndPuts.push(callAndPut);
stock.set(callAndPut.symbol, stockCallsAndPuts);
}
There are a few ways to go about this, and the best depends on how the data needs to be processed later, but from your description I'd go with something along the lines of
var stocks = {};
var stockCallsPut = {};
// loop over stocks and actions
if (!(symbol in stocks)) {
stocks[symbol] = [];
}
if (!(symbol in stockCallsPut)) {
stockCallsPut[symbol] = {};
}
if (!(symbolType in stockCallsPut[symbol])) {
stockCallsPut[symbol][symbolType] = [];
}
// accumulated stock json items here
stocks[symbol].push(new_stock_item);
// accumulated push/call json items of stock here
stockCallsPut[symbol][symbolType].push(new_action);
I'm still not sure I actually understood what your data looks like, but sounds kind of like this to me:
// Not sure if data is an object or array
var data = {
'one': {
'name': 'one-somename',
'number': 'one-somenumber',
'symbol': 'C'
},
'two': {
'name': 'two-somename',
'number': 'two-somenumber',
'symbol': 'P'
},
'three': {
'name': 'three-somename',
'number': 'three-somenumber',
'symbol': 'C'
}
};
var stocks = {};
for (var name in data) {
// It sounded like you wanted a call/put array for each object but I'm not sure if that's true since it wouldn't be possible... if so can just divide this part up into it's appropriate place in the if statement below
// Checking that the property is set on the object, if it is, it uses itself, otherwise it adds it with the call/put arrays created
stocks[name] = stocks[name] ? stocks[name] : {'calls': [], 'puts': []};
var type;
if (data[name]['symbol'] === 'C') {
type = 'calls';
} else if (data[name]['symbol'] === 'P') {
type = 'puts';
}
stocks[name][type].push(data[name]);
}

Update a specific record in Firebase using angularJS

How I can access the whole object where phoneNo = 222 and update its lang and lat
var db = new Firebase("myFB/names");
names {
-K7VQBTELD4FQE1HL1U{
phoneNo: 111,
long: 126,
lat: 458
}
-K7WJGBTELD4FQ1EHL1U{
phoneNo: 222,
long: 547,
lat: 951
}
}
A Firebase query requires an order by function, then you can restrict to a criteria like equalTo().
var ref = new Firebase("<my-firebase-app>/names");
var query = ref.orderyByChild('phoneNo').equalTo('222');
Then you can create a listener that will retrieve the value. The value will come back as an DataSnapshot, which contains the associated reference. Using that reference you can process whatever updates you need.
query.on('value', function(snap) {
var obj = snap.val();
var snapRef = snap.ref();
snapRef.update({
lat: 32.332325,
long: 124.2352352
});
});
However, you don't need to reed the object to update it. In your case, you're using a push-id as a key for /names. Perhaps an easier lookup would be the phoneNo key. Then you could do the following:
var ref = new Firebase('https://my-firebase-app/names/222');
ref.update({
lat: 32.332325,
long: 124.2352352
});

Trouble Pivoting data with Map Reduce

I am having trouble pivoting my dataset with map reduce. I've been using the MongoDB cookbook for help, but I'm getting some weird errors. I want to take the below collection and pivot it so that each user has a list of all of the review ratings.
My collection looks like this:
{
'type': 'review',
'business_id': (encrypted business id),
'user_id': (encrypted user id),
'stars': (star rating),
'text': (review text),
}
Map function (wrapped in Python):
map = Code(""""
function(){
key = {user : this.user_id};
value = {ratings: [this.business_id, this.stars]};
emit(key, value);
}
""")
The map function should return an array of values associated with the key...
Reduce function (wrapped in Python):
reduce = Code("""
function(key, values){
var result = { value: [] };
temp = [];
for (var i = 0; i < values.length; i++){
temp.push(values[i].ratings);
}
result.value = temp;
return result;
}
""")
However, the results return one less rating than total. In fact, some users have None returned, which can't happen. Some entries look like the following:
{u'_id': {u'user: u'zwZytzNIayFoQVEG8Xcvxw'}, u'value': [None, [u'e9nN4XxjdHj4qtKCOPQ_vg', 3.0], None, [...]...]
I can't pinpoint what in my code is causing this. If there are 3 reviews, they all have business IDs and ratings in the document. Plus, using 'values.length + 1' in my loop condition breaks values[i] for some reason.
Edit 1
I've embraced the fact that reduce gets called multiple times on itself, so below is my new reducer. This returns an array of [business, rating, business, rating]. Any idea how to output [business, rating] arrays instead of one giant array?
function(key, value){
var result = { ratings:[] };
var temp = [];
values.forEach(function(value){
value.ratings.forEach(function(rating){
if(temp.indexof(rating) == -1){
temp.push(rating);
}
});
});
result. rartings = temp;
return result;
}
Heres a test example:
1) Add some sample data:
db.test.drop();
db.test.insert(
[{
'type': 'review',
'business_id': 1,
'user_id': 1,
'stars': 1,
},
{
'type': 'review',
'business_id': 2,
'user_id': 1,
'stars': 2,
},
{
'type': 'review',
'business_id': 2,
'user_id': 2,
'stars': 3,
}]
);
2) Map function
var map = function() {
emit(this.user_id, [[this.business_id, this.stars]]);
};
Here we set the results as we want them to look like at the end of the process. Why? because if there is only ever a single review by a user (the key we are grouping by) then the results won't go through a reduce phase.
3) Reduce function
var reduce = function(key, values) {
var result = { ratings: [] };
values.forEach(function(value){
result.ratings.push(value[0]);
});
return result;
};
Here we collect up all the values, remembering we nested them in the map method, so we can just pick out the first value for each set of results.
4) Run the map reduce:
db.test.mapReduce(map, reduce, {finalize: final, out: { inline: 1 }});
Alternative - use the aggregation framework:
db.test.aggregate({
$group: {
_id: "$user_id",
ratings: {$addToSet: {business_id: "$business_id", stars: "$stars"}}
}
});

Categories