I've been trying to translate this query into MapReduce for a few days. Specifically, I need to figure out how many different cars have driven "N" kilometers.
Query:
db.adsb.group({
"key": {
"KM": true
},
"initial": {
"countCar": 0
},
"reduce": function(obj, prev) {
if (obj.Matricula != null) if (obj.Matricula instanceof Array) prev.countCar += obj.Matricula.length;
else prev.countCar++;
},
"cond": {
"KM": {
"$gt": 10000,
"$lt": 45000
}
}
});
Each document in Mongo has this form:
{
"_id" : ObjectId("5a8843e7d79a740f272ccc0a"),
"KM" : 45782,
"Matricula" : "3687KTS",
}
I'm trying to get something like:
/* 0 */
{
“KM” : 45000,
“total” : 634
}
/* 1 */
{
“KM” : 46000,
“total” : 784
}
My code is below, and it compiles but does not give me the expected results.
In particular, every time I enter 'reduce' it seems to reset all the values to 0, which prevents me from accumulating the registrations.
One of my problems is that when handling large amounts of information, the function must iterate several times ' reduce'.
I also don't know if it could be done that way, or I would need to return a list of car plates and their counter together in 'reduce'; and then in finalize add it all up.
// Map function
var m = function() {
if (this.KM > 10000 && this.KM < 45000) { // So that i can get KM grouped together by thousands (10000, 20000, 30000...)
var fl = Math.round(this.KM / 1000) * 1000;
var car = this.Matricula
emit (fl, car);
//print("map KM=" + fl + " Matricula= " + car);
}
};
// Reduce function
var r = function(key, values) {
var ya_incluido = false;
var cars_totales = 0;
var lista_car = new Array();
//print( key + " ---- " + values);
for (var i=0; i < values.length;i++)
{
for (var j=0; j < lista_car.length;j++)
{
if(values[i] == lista_car[j]) { //If it is already included, don't aggregate it
ya_incluido = true;
}
} if (ya_incluido != true) { //If it is not included, add it to lista_av list.
lista_car.push(values[i]);
} ya_incluido = false;
}
cars_totales = lista_av.length; //The number of distinct cars is equal to the lenght of the list we created
return cars_totales;
};
// Finalize function
var f = function(key,value) {
// Sum up the results?
}
db.runCommand( {
mapReduce: "dealer",
map: m,
reduce: r,
finalize: f,
out: {replace : "result"}
} );
I found the answer and a really good explanation here: https://stackoverflow.com/a/27532153/13474284
I found the answer and a really good explanation here: https://stackoverflow.com/a/27532153/13474284
I couldn't find a way to return in 'reduce' the same thing that came from ' map' . And since it was run several times, it only got the results of the last iteration. The way it appears in the link, the problem is solved without any difficulty.
Related
I need bit help on below issue.
Background:
I have a set of array.
["3a67ef_V1", "3a67ef", "2b678_V1", "1b", "3a67ef_V2", "2b678_V2", "2b678"]
in this array i need to find the curentRecordName andhigher Version numeber
I am creating a file based on record id
For e.g: if record id is 1 then file will be saved as 1a.json
if user want to create another record with same name
i will write as 1a_V1.json
Update: there can be other character as well besides number, but after _V there wont be other letters than numbers
Issue: whenever user is creating same record name file should write as
1a_V1.json
1a_V2.json
1a_V3.json
1a_V4.json
Code goes here
// current record name
var curentRecordName = '2';
//we need check curentRecordName as well as higher version in this array
var recordIDs = ["3a67ef_V1", "3a67ef", "2b678_V1", "1b", "3a67ef_V2", "2b678_V2", "2b678"];
//looping though the array
for (var m = 0; m < recordIDs.length; m++) {
var version = +1;
// if record exist, add record to initial version to 1
// for e.g: if record number 4 is alreday exist then create 4_V1
if (recordIDs[m] === curentRecordName) {
curentRecordName = curentRecordName + '_V' + [version];
break;
}
//if record version 2 exist add version 3, if version 3 exist add version 4 etc
// i need help here please
if (recordIDs[m] === curentRecordName + '_V' + recordIDs[m].split("_V").pop()) {
var adddition = parseInt(recordIDs[m].split("_V").pop()) + 1;
curentRecordName = curentRecordName + '_V' + adddition;
break;
}
}
console.log(curentRecordName);
//Final motive is to create _V3, _V4_, V5 in arrays
Once record name is added, i will save it background
var saveObject = {
"alf_destination": localStorage.getItem('folderNodeRef'),
"prop_cm_name": "report_" + recordName + ".json",
"prop_cm_content": JSON.stringify(this.records),
"prop_mimetype": "text/json"
};
how to do this ?
I just want a logic how to create records by finding high version in array and adding one more bigger version.?
JSFiddle link
any help is appreciated.
You can try using this
// current record name
var curentRecordName = '2';
//we need check curentRecordName as well as higher version in this array
var recordIDs = ["3_V1", "3", "2_V1", "1", "3_V2", "2_V2", "2"];
var possibleIDs = recordIDs.filter((data) => data.split("_")[0] == curentRecordName).sort((first, second) => {
return second.split("_V")[1] - first.split("_V")[1];
});
if (possibleIDs.length > 0) {
var data = possibleIDs[0].split("_V")[0] + "_V" + (Number(possibleIDs[0].split("_V")[0]) + 1);
recordIDs.push(data);
}
console.log(recordIDs);
by finding high version in array and adding one more bigger version
In ES6 you can do that :
https://jsfiddle.net/xjx6koda/
//we need check curentRecordName as well as higher version in this array
var recordIDs = ["3a67ef_V1", "3a67ef", "2b678_V1", "1b", "3a67ef_V2", "2b678_V2", "2b678"];
function getNextRecord(crn) {
const [highestVersion] = recordIDs
.filter(r => r.indexOf(`${crn}_V`) === 0 || crn === r)
.map(r => (crn === r) ? 0 : parseFloat(r.replace(`${crn}_V`,'')) )
.sort((a,b) => a < b)
return highestVersion >= 0 ? `${crn}_V${highestVersion + 1}` : crn;
}
console.log('with 3a67ef : ', getNextRecord('3a67ef'))
// with 3a67ef : 3a67ef_V3
console.log('with new : ', getNextRecord('new'))
// with new : new
console.log('with 1b : ', getNextRecord('1b'))
// with 1b : 1b_V1
So I am trying to sort my array with objects based on an objects value..
var mostCheapHistory [ { lowestTitle: title goes here, lowestPrice: 100 }, another obj, another, another } ]
And I want to sort them based on their price, so I came up with this:
var historyObj = mostCheapHistory[0];
for(var y in mostCheapHistory){
nextObj = mostCheapHistory[y];
console.log('Is '+ historyObj.lowestPrice + ' more as ' + nextObj.lowestPrice + ' ?');
console.log(historyObj.lowestPrice > nextObj.lowestPrice);
console.log('-----')
}
And this is the output...
Is 124.98 more as 124.98 ?
false
-----
Is 124.98 more as 18.59 ?
false
-----
Is 124.98 more as 25.9 ?
false
-----
Is 124.98 more as 26.99 ?
false
-----
Is 124.98 more as 34.76 ?
false
-----
What the hell is going on? It's obvious that 124.98 is more as 34.76 and yet it gives false?
The array is made using this code:
for(var x=0; x < items.length; x++){
var title = items[x].title[0];
var price = items[x].sellingStatus[0].currentPrice[0].__value__;
var item =
{
lowestPrice : price,
lowestTitle : title
}
if(x === 0){
mostCheapHistory.push(item);
}
else{
for(var y=0; y < mostCheapHistory.length; y++){
if(mostCheapHistory[y].lowestPrice > price ){
if(mostCheapHistory.length < 5){
mostCheapHistory.push(item);
break;
}
else{
mostCheapHistory[y] = item;
break;
}
}
}
}
}
Use the predefined sort function :
mostCheapHistory.sort(function(a,b){
return a.lowestPrice - b.lowestPrice;
}
+ Your code might be pushing strings rather than numbers, which explains why 124.98 > 34.76 => true but '124.98' > '34.76' => false, since the string '34.76' is greater than '124.98' in string comparison.
Use parseFloat() for the prices then check again.
It looks like, you are comparing strings instead of numerical values. If you say (in comments), that the sorting function
array.sort((a, b) => a.lowestPrice - b.lowestPrice);
solves your problem, then while this sort callback uses an implicit casting to number with the minus - operator.
The result is a sorted array with values as string.
You can sort these based on price using Array.prototype.sort() (See MDN documentation)
e.g.
var data = [
{
title: "title1",
value: 123
},
{
title: "title2",
value: 324
},
{
title: "title3",
value: 142
}];
var sorted = data.sort(function(a, b) {
return a.value > b.value;
});
I think the type of lowestPrice is string not float, use parseFloat(historyObj.lowestPrice) to compare the two values
Trying to implement this (marcog's answer) overlapping intervals algorithm in Javascript but I can't get it working.
I want to go over the values in a JSON data structure picking out start stop values representing x1-x2 coordinates for lines. Im adding new lines one after the other and I want to know when adding a new line would result in overlapping lines.
The error I'm getting is that it always prints "no overlaps" when there clearly are overlaps.
This is the code I have so far:
var
data = [],
json = [
{
"start" : 100,
"stop" : 800
},
{
"start" : 900,
"stop" : 1200
},
{
"start" : 200,
"stop" : 600
}
],
sortInterval, checkOverlappingInterval;
sortInterval = function (value) {
//Sorts a list with lower numbers first and end point come before
//starting points on ties
value.sort(function (a,b){
var aSplit = a.split("_"),
bSplit = b.split("_");
if (aSplit[0] * 1 > bSplit[0] * 1){
return 1;
}
if (aSplit[0] * 1 < bSplit[0] * 1) {
return -1;
} else {
if (aSplit[1] > bSplit[1]) {
return 1;
} else {
return -1;
}
}
});
};
checkOverlappingInterval = function(value){
//Return true if there are overlapps
var inInterval = false;
value.forEach(function(v) {
if (v.charAt(v.length-1) === "S"){
if(inInterval){
return true;
}
else {
inInterval = true;
}
}
else {
inInterval = false;
}
});
//return true;
return false;
};
json.forEach(function (value) {
//Push the new values to interval array and sort the array
data.push(value.start + "_S");
data.push(value.stop + "_E");
sortInterval(data);
//Check to see if the new line caused an overlapping line
//If it did increase y and clear out data
if (checkOverlappingInterval(data)){
console.log("overlaps");
}
//If it did not print line
else {
console.log("no overlaps");
}
});
Two mistakes:
You forgot to return 0 from your compasion function in case of a parity. See Sorting in JavaScript: Should every compare function have a "return 0" statement?
You are trying to return true from the forEach callback. That will only return from the current callback, not from the checkOverlappingInterval function. Use every/some, or a normal for loop instead.
I believe this should work:
1) Sort the json array by start value
2) I know for sure that the start will always be greater than all the previous starts, so the only thing I have to check is if the stop from any previous is greater than the current start I'm checking. I do that with the for and I keep the max stop in a varibale. So if the current start is greater than the max I had, it overlaps
json = [
{
"start" : 100,
"stop" : 800
},
{
"start" : 900,
"stop" : 1200
},
{
"start" : 200,
"stop" : 600
},
{"start":700, "stop":800}
];
function checkOverlaps(arr){
arr=arr.slice(0);
arr.sort(function(a,b){return a.start-b.start});
var max=0;
for(var i=1;i<arr.length;i++){
max=arr[i-1].stop > max ? arr[i-1].stop : max;
if(arr[i].start < max){
console.log(arr[i],"overlaps");
}
}
}
checkOverlaps(json);
I have created a working javascript function to check an array of 5 numbers for a small straight, in a Yahtzee game I'm making. I've tested it to no end and I'm confident it works 100% of the time, but it is also probably the worst algorithm of all time in terms of being efficient. Here is what it looks like:
function calcSmstraight() {
var sum = 0;
var r = new Array();
var r2 = new Array();
var counter = 0;
var temp;
var bool = false;
var bool2 = false;
r[0] = document.getElementById('setKeep1').value;
r[1] = document.getElementById('setKeep2').value;
r[2] = document.getElementById('setKeep3').value;
r[3] = document.getElementById('setKeep4').value;
r[4] = document.getElementById('setKeep5').value;
// Move non-duplicates to new array
r2[0] = r[0];
for(var i=0; i<r.length; i++) {
for(var j=0; j<r2.length; j++) {
if(r[i] == r2[j]) {
bool2 = true; // Already in new list
}
}
// Add to new list if not already in it
if(!bool2) {
r2.push(r[i]);
}
bool2 = false;
}
// Make sure list has at least 4 different numbers
if(r2.length >= 4) {
// Sort dice from least to greatest
while(counter < r2.length) {
if(r2[counter] > r2[counter+1]) {
temp = r2[counter];
r2[counter] = r2[counter+1];
r2[counter+1] = temp;
counter = 0;
} else {
counter++;
}
}
// Check if the dice are in order
if(((r2[0] == (r2[1]-1)) && (r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)))
|| ((r2[1] == (r2[2]-1)) && (r2[2] == (r2[3]-1)) && (r2[3] == (r2[4]-1)))) {
bool = true;
}
}
if(bool) {
// If small straight give 30 points
sum = 30;
}
return sum;
}
My strategy is to:
1) Remove duplicates by adding numbers to a new array as they occur
2) Make sure the new array is at least 4 in length (4 different numbers)
3) Sort the array from least to greatest
4) Check if the first 4 OR last 4 (if 5 in length) numbers are in order
My question:
Does anyone know a way that I can improve this method? It seems ridiculously terrible to me but I can't think of a better way to do this and it at least works.
Given that you're implementing a Yahtzee game you presumably need to test for other patterns beyond just small straights, so it would be better to create the array of values before calling the function so that you can use them in all tests, rather than getting the values from the DOM elements inside the small straight test.
Anyway, here's the first way that came to my mind to test for a small straight within an array representing the values of five six-sided dice:
// assume r is an array with the values from the dice
r.sort();
if (/1234|2345|3456/.test(r.join("").replace(/(.)\1/,"$1") {
// is a small straight
}
Note that you can sort an array of numbers using this code:
r2.sort(function(a,b){return a-b;});
...but in your case the values in the array are strings because they came from the .value attribute of DOM elements, so a default string sort will work with r2.sort(). Either way you don't need your own sort routine, because JavaScript provides one.
EDIT: If you assume that you can just put the five values as a string as above you can implement tests for all possible combinations as a big if/else like this:
r.sort();
r = r.join("");
if (/(.)\1{4}/.test(r)) {
alert("Five of a Kind");
} else if (/(.)\1{3}/.test(r)) {
alert("Four of a Kind");
} else if (/(.)\1{2}(.)\2|(.)\3(.)\4{2}/.test(r)) {
alert("Full House");
} else if (/(.)\1{2}/.test(r)) {
alert("Three of a Kind");
} else if (/1234|2345|3456/.test( r.replace(/(.)\1/,"$1") ) {
alert("Small Straight");
} // etc.
Demo: http://jsfiddle.net/4Qzfw/
Why don't you just have a six-element array of booleans indicating whether a number is present, then check 1-4, 2-5, and 3-6 for being all true? In pseudocode:
numFlags = array(6);
foreach(dice)
numFlags[die.value-1] = true;
if(numFlags[0] && numFlags[1] && numFlags[2] && numFlags[3]) return true
//Repeat for 1-4 and 2-5
return false
This wouldn't be a useful algorithm if you were using million-sided dice, but for six-siders there are only three possible small straights to check for, so it's simple and straightforward.
I do not play Yahtzee, but I do play cards, and it would appear the algorithm might be similar. This routine, written in ActionScript (my JavaScript is a bit rusty) has been compiled but not tested. It should accept 5 cards for input, and return a message for either straights greater than 3 cards or pairs or higher.
private function checkCards(card1:int,card2:int,card3:int,card4:int,card5:int):String
{
// Assumes that the 5 cards have a value between 0-12 (Ace-King)
//The key to the routine is using the card values as pointers into an array of possible card values.
var aryCardValues:Array = new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
aryCardValues[card1] += 1;
var aryCardNames:Array = new Array("Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King");
var strOutMessage:String;
var intCardCount:int = 0;
var strSeperator:String;
var strHighCard:String;
for (var i:int = 0;i < aryCardValues.length;i++)
{
//Check for runs of three of a kind or greater.
if (aryCardValues[i] >= 2)
{
strOutMessage = strOutMessage + strSeperator + i + "-" + aryCardNames[i] + "s";
strSeperator = " & ";
}
//Check for values in a straight.
if (aryCardValues[i] > 0)
{
intCardCount++;
if (intCardCount > 3)strHighCard = aryCardNames[i];
}
else
{
if (intCardCount < 3)intCardCount = 0;
}
}
if (intCardCount > 3) strOutMessage = intCardCount + " run " + strHighCard + " High."
return strOutMessage;
}
It may not be as concise as the regular expressions used above, but it might be more readable and easily modified. One change that could be made is to pass in an array of cards rather than discrete variables for each card.
I need to create the following array dynamically, for example:
var data = { point: [
{ x:5, y:8 },
{ x:8, y:10},
]};
console.log(data.point[0].x); // 5 n=0
console.log(data.point[1].y); // 10 n=1
At some point my application needs to expand the array to more than 2 items (n=0, n=1). Please let me know how to do that (i.e. n = 9 ).
You could use Array.push method to add element to an array.
var point = {x:1,y:1};
data.point.push(point);
you can use method 'push' like this code
var data = { point: [
{ x:5, y:8 },
{ x:8, y:10},
]};
console.log(data.point[0].x); // 5 n=0
console.log(data.point[1].y); // 10 n=1
data.point.push({x:4,y:3});
console.log(JSON.stringify(data.point));
You could do something like this:
var data = {'points' : []};
function addPoint(x, y) {
data.points.push({'x' : x, 'y' : y});
}
function getPoint(index) {
return data.points[index];
}
addPoint(10, 20);
addPoint(5, 3);
var p = getPoint(1);
alert(p.x + ", " + p.y); //alerts => "5, 3"
This would work for any arbitrary number of points.
Update
To clear the array
function clearPoints() {
data.points = [];
}
Update 2
These little functions will work okay if you have a simple page. If this points handling is going to end up being part of a larger system, it may be better to do something like this:
var data = {
'points' : [],
addPoint : function(x, y) {
this.points.push({
'x' : x,
'y' : y
});
},
getPoint : function(index) {
return this.points[index];
},
clearPoints : function() {
this.points = [];
},
removePoint : function(index) {
this.points.splice(index, 1);
}
};
Example usage:
alert(data.points.length); // => 0
data.addPoint(1, 2);
data.addPoint(8, 12);
data.addPoint(3, 7);
alert(data.points.length); // => 3
var p = data.getPoint(2); //p = {x:3, y:7};
data.removePoint(1);
alert(data.points.length); // => 2
data.clearPoints();
alert(data.points.length); // => 0
It may allow you to keep your point handling a little cleaner and easier to use and update.
You can either use the push() function as stated, or add additional items to the array using an index, which is preferred in the Google Style Guide. The latter method does require that you have a pointer to the last index.
Note that since assigning values to an array is faster than using
push() you should use assignment where possible.
for(var i=lastIndex; i < numOfNewPoints; i++){
data.point[i] = {x:4, y:3};
}