This was intended to create a 2d array of objects which would allow me to access any of the objects (the "clips") by banks[a][b], where [a] was a "bank" and [b] was a "clip". Works perfectly as - is, unfortunately this code is meant to look at some external files and see their properties. These files are already organized in an "array" based on some of their properties. Originally I was told this would be an 8x8 array, however now it turns out that this would be a 16x32 array and the requirements specify banks composed of 4x2 selections from the array.
In other words,
banks[0][0].track = 0
banks[0][0].slot = 0
banks[0][3].track = 3
banks[0][3].slot = 0
banks[0][4].track = 0
banks[0][4].slot = 1
banks[0][7].track = 3
banks[0][7].slot = 1
banks[15][0].track = 0
banks[15][0].slot = 31
banks[15][3].track = 3
banks[15][3].slot = 31
banks[16][0].track = 3
banks[16][0].slot = 0
banks[16][4].track = 3
banks[16][4].slot = 1
banks[63][0].track = 11
banks[63][0].slot = 30
banks[63][4].track = 11
banks[63][4].slot = 31
I need to iteratively create a 64x8 2d array of "clips", but at the same time set the above properties of those clips as shown. It seems clear that the relevant math belongs in the clip object. However, I can't see the math yet. Any suggestions would be greatly appreciated.
Sounds like you need this:
var theirBanks = [[...], [...], ...]; // 16*32 array of Clip objects
var x = 2; // reduce outer array to one of half length
var y = 4; // reduce inner arrays to one of fourth length
function reduce(sel) {
/* gets: a 2*4 selection of clips from theirBanks
returns: a new clip for banks */
... // not sure how you want this to be done
}
var banks = new Array(theirBanks.length / x);
for (var i=0; i<banks.length; i++) {
banks[i] = [];
for (var j=0; j<theirBanks.length/y; j++) {
var selection = [];
for (var k=i*x; k<(i+1)*x; k++)
selection.push(theirBanks[k].slice(j*y, (j+1)*y));
banks[i][j] = reduce(selection);
}
}
// banks is now a 8*8 array
The script builds 2*4 selections from the (two-dimensional) array, lets you reduce them to a new object and returns the new, smaller (two-dimensional) array.
Related
I'm trying to crate a code that will count number of images I have and then will create a list with the number of images (for example, if I have 5 images, I'll get this :
[0,1,2,3,4].
My code runs and I think it creates a list that is empty:
This is my code (I have tried to put only the relevant part):
//First I filter my image collection according to the number of pixels each image has
//Filter according to number of pixels
var ndviWithCount = withNDVI.map(function(image){
var countpixels = ee.Number(image.reduceRegion({
reducer: ee.Reducer.count(),
geometry: geometry,
crs: 'EPSG:4326',
scale: 30,
}).get('NDVI'));
return image.set('count', countpixels);
});
print(ndviWithCount, 'ndviWithCount');
//Here I count what is the maximum number of pixels that image has and then I create new collection with //only "big images"
var max = ndviWithCount.reduceColumns(ee.Reducer.max(), ["count"]);
print(max.get('max'));
var max_pix=max.get('max');
//filter between a range
var filter = ndviWithCount.filter(ee.Filter.rangeContains(
'count', max_pix, max_pix));
print(filter, 'filtered');
//Here I try to grab the number of images so I can create a list
var num_images=filter.size();
//creating the list of images
var listOfImages =(filter.toList(filter.size()));
//Here is the loop that diesn't work
//I have tried to determine i=0, and then that it will iterate untill i is equal to the number of images
//I try to say, i=0, so add 1 and the nadd it to my list.
for (i = 0; i < num_images; i++) {
var listOfNumbers=[];
i=i.add(1);
listOfNumbers.push(i);
}
my end goal is to have list that contains nmbers from 0 or 1 to the number of images I have.
for (i = 0; i < num_images; i++) {
var listOfNumbers=[];
i=i.add(1);
listOfNumbers.push(i);
}
You are initiating the array inside the for loop which isn't what you intend. I'm also not sure what i = i.add(1) is supposed to be. May be helpful to check out info on for loops.
let listOfNumbers = [];
for (let i = 0; i < num_images; i++) {
listOfNumbers.push(i);
}
I found the solution. The reason for the error is because of the GEE envorinment.
This code works:
var serverList = ee.List.sequence(0,NumberOfImages.subtract(1));
serverList = serverList.map(function(n) {
return ee.Number(n).add(1);
});
print(serverList);
In Google Earth Engine Developer's Guide, there is a recommendation to avoid for() loops. They recommend to use map() function as this example:
// to avoid
var clientList = [];
for(var i = 0; i < 8; i++) {
clientList.push(i + 1);
}
print(clientList);
// to use
var serverList = ee.List.sequence(0, 7);
serverList = serverList.map(function(n) {
return ee.Number(n).add(1);
});
print(serverList);
I'm trying to select MODIS scenes from each month/year prior to compute VCI. So, the approach I'd take is with a double loop:
modis = ee.ImageCollection("MODIS/MYD13A1");
var modis_list = [];
for(var i = 1; i <13; i++) {
for(var j = 2000; j <2018; j++){
modis_list.push(modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year')));
}
}
print(modis_list);
Is there a way to replicate a double loop like this with map() function to reach a server-side approach?
The easy way to do this is with a single map over the "months" you care about.
// Collect images for each month, starting from 2000-01-01.
var months = ee.List.sequence(0, 18*12).map(function(n) {
var start = ee.Date('2000-01-01').advance(n, 'month')
var end = start.advance(1, 'month')
return ee.ImageCollection("MODIS/MYD13A1").filterDate(start, end)
})
print(months.get(95))
This will return a list of ImageCollections. Most months will have only 1 image, since MYD13A1 contains 16-day images, but some will have two. Month 95 is Jan of 2008 and has two.
Alternatively, you could join the collection with a collection of dates, but this is simpler.
And you should prefer filterDate over calendarRange when possible, as it's optimized.
Assuming that you are just trying to understand GEE's map() function, and how would be the equivalent of a normal js for loop, the code would be:
var map_m = function(i) {
i = ee.Number(i)
var years = ee.List.sequence(2000, 2017)
var filtered_col = years.map(function(j) {
var filtered = modis.filter(ee.Filter.calendarRange(i, i, 'month'))
.filter(ee.Filter.calendarRange(j, j, 'year'))
return filtered
})
return filtered_col
}
var months = ee.List.sequence(1, 12)
var modis_list2 = months.map(map_m).flatten()
This code replicates a normal for loop. First, it goes item by item of the years list, and then item by item of the months list, and then, once you have year and month, filter the collection and add it to a list (map does that automatically). As you use 2 map functions (one over years and the other over months), you get a list of lists, so to get a list of ImageCollection use the flatten() function. Somehow the printed objects are a bit different, but I am sure the result is the same.
Let me start by saying I know nothing about Google Earth Engine and my info is from functional programming knowledge.
map is unique in that it doesn't generate the things it loops over. You start with a list and map iterates over each item in that list and transforms it. If you don't have that list then map isn't a great fit.
It looks like you are creating a list with each month/year combo represented. I would break this into a few steps. Build the month and year lists, build the list that represents cartesian product of the 2 lists then transform to the ee objects.
var range = (from, to) => new Array(end-start+1).fill(0).map((_,i)=>i+from)
var cartesianProduct = (a, b) => // not gonna do this here but it returns pairs [ [ a[1], b[1] ], ... ]
var getEE = ([month, year]) => modis
.filter(ee.Filter.calendarRange(month, month, 'month'))
.filter(ee.Filter.calendarRange(year, year, 'year'));
var months = range(1,12);
var years = range(2000, 2017);
var data = cartesianProduct(months, years)
.map(getEE)
There are likely better ways(like not iterating throught the whole list of modis each time we want a single month object (use a dictionary)) but the gist is the same.
I'm attempting to use the build in Functions in OrientDB studio to group Workstations that aren't in use by a Person. The query to get these vertices works fine but I'm trying to avoid Traverse as it is very slow - too slow to be used in production. Instead of iterating through each free station and grouping it together with all it's neighbours, keeping each grouped 'named' with the smallest #rid in the set.
var groups = {}; //The list of groups of workpoints. The key is the lowest RID in the group
var mappedDesks = {}; //Every desk's RID is in this object with it's matching value being the group name they're in
//Get all Workpoints that don't have a Locale CURRENTLY_LOCATED_ON them
var freeDesks = db.query("SELECT FROM Workpoint WHERE #rid NOT IN (SELECT #rid FROM (SELECT EXPAND(OUT('CURRENTLY_LOCATED_ON').OUT('LOCATED_ON')) FROM Person) WHERE #class = 'Workpoint')");
//Iterate through all vacant Workpoints
for (var j=0; j < freeDesks.length; j++){
var baseNodeRid = freeDesks[j].getRecord().getIdentity().toString(); // The RID of the Workpoint
var baseNodeNumber = parseFloat(baseNodeRid.replace("#", "").replace(":",".")); // The RID converted to a number for comparisons. The lower RID takes precedence
var baseSanitized = baseNodeRid.replace(":","-") // Keys cannot contain colon so they are replaced with a dash
if (freeDesks[j].getRecord().field("out_NEIGHBOUR_OF") == null ) {
// Desks without neighbours can be put in a group on their own
groups[baseSanitized] = new Array();
groups[baseSanitized].push(baseNodeRid);
mappedDesks[baseSanitized] = baseSanitized;
} else {
//Iterate over all the desk's neighbours
for (var n = 0; n < freeDesks[j].getRecord().field("out_NEIGHBOUR_OF").length; n++){
//Convert the neighbour's RID to a number too
var nSanitized = n.replace(":","-");
if (parseFloat(n.replace("#", "").replace(":",".")) > baseNodeNumber ){
//The neighbour's node group is larger than the current one. This needs to be merged into the group with the smaller rid
//Move the desks from the neighbour's group into the base's group. If it has none then do nothing
var nGroup = groups[mappedDesks[nSanitized]]
if ( nGroup != null) {
groups[baseSanitized] = groups[baseSanitized].concat(nGroup);
//Change the mapping of each moved desk to the base's
for (var g = 0; g < nGroup.length; g++){
mappedDesks[nGroup[g]] = baseSanitized;
}
}
//Delete the reference to the old group
delete groups[mappedDesks[nSanitized]];
//Update the mappings for the desks dealt with
mappedDesks[nSanitized] = baseSanitized;
mappedDesks[baseSanitized] = baseSanitized;
} else {
// The neighbour is lower than the current desk so the desk should be merged into the neighbour's group
mappedDesks[baseSanitized] = nSanitized;
groups[nSanitized].push(baseNodeRid);
}
}
}
}
return groups;
My problem comes from accessing a vertex's neighbours. It correctly determines whether there are neighbours in the if statement return freeDesks[j].getRecord().field("out_NEIGHBOUR_OF") but I want to be able to get each neighbour's #rid so I can sort the #rids into groups.
freeDesks[j].getRecord().field("out_NEIGHBOUR_OF") returns the edge record but I dont seem to be able to get the "in" or "out" fields using the field() method (not found on this object) or accessing it as an array [].
[
{
"#type": "d",
"#rid": "#34:18176",
"#version": 6,
"#class": "NEIGHBOUR_OF",
"out": "#16:13",
"in": "#16:1408",
"#fieldTypes": "out=x,in=x"
}
]
Can you help be get a list/array of the neighbour #rids so I can iterate over them with the rest of the code?
Cheers!
A simpler example to understand what you can do:
create class Person extends V
create class IsNeighbour extends E
create vertex Person set name = 'P1' // 12:0
create vertex Person set name = 'P2' // 12:1
create vertex Person set name = 'P3' // 12:2
create edge IsNeighbour from #12:0 to #12:1
create edge IsNeighbour from #12:0 to #12:2
Define this Javascript Function:
var gdb = orient.getGraphNoTx();
var v = gdb.command("sql", "select from " + personRID);
var neighbours = v[0].getRecord().field("out_IsNeighbour").iterator();
var result = [];
while(neighbours.hasNext()){
var neighbour = neighbours.next();
result.push(neighbour.field("in"));
}
return result;
like this:
And then you can:
select getNeighbours("#12:0")
var klas4 = [];
klas4[2] = [];
klas4[2]["hour"] = 1;
klas4[2]["teacher"] = "JAG";
klas4[2]["group"] = "V4A";
klas4[2]["subject"] = "IN";
klas4[2]["classroom"] = "B111";
klas4[0] = [];
klas4[0]["hour"] = 6;
klas4[0]["teacher"] = "JAG";
klas4[0]["group"] = "V4B";
klas4[0]["subject"] = "IN";
klas4[0]["classroom"] = "B111";
klas4[1] = [];
klas4[1]["hour"] = 4;
klas4[1]["teacher"] = "NAG";
klas4[1]["group"] = "V4A";
klas4[1]["subject"] = "NA";
klas4[1]["classroom"] = "B309";
This multidimensional array needs to be sorted by hour, ascending. The problem is, I don't know how to sort an multidimensional array. The first dimension (0, 1 and 2), needs to be changed, according to the hour, but all other details from dimension 2 (teacher, group etc.) also need to change from index, because otherwise the data is mixed.
You don't know how many indexes there are. In this example, the correct sequence should be: klas4[2][...], klas4[1][...], klas[0][...]
In PHP there's a certain function multisort, but I couldn't find this in jQuery or JavaScript.
klas4.sort( function(a,b){ return a.hour - b.hour } );
should do it.
It helps to think of klas4 not as a multi-array but as 1 array of objects.
Then you sort the objects in that array with a sort function.
The sort function takes 2 objects and you must return which one comes first.
You should read on sort() for Array, google that.
Also, as others have commented; the entries for klas4 are really objects, you should use
klas4[2] = {};
or even better
klas4[2] = { hour:1 , teacher:"JAG" , group:"V4A" , subject: "IN" };
Finally, I assume you are a native Dutch or German speaker, as I am. I would strongly suggest to name all your variables in English, class4, not klas4. It is the right, professional thing to do.
I'm trying to load up an array of objects that is an array itself, so to speak. So I have an array of names(members) then I'm making a master list that will have name, bugs, enhance, etc. in one row for every name. So I run a simple for loop to load master list but it is telling me that for (0) i, masterList[i].name cannot be loaded because it is undefined. Is there a way around this? Thanks
var mem = getMembers();//get a array of members
var memlen = mem.length;
var masterList = [memlen]; //set to number of members
for(i=0; i < memlen; i++){
masterList[i].name = mem[i]; //set .name to the members list name at i
masterList[i].bugs = 0;
masterList[i].enhance = 0;
masterList[i].epic = 0;
masterList[i].dev = 0;
masterList[i].high = 0;
}
To create your array with a predefined size, use this :
masterList = new Array(memlen);
(with masterList = [memlen] you were just creating an array whose first item is memlen. This wasn't so problematic because the array was automatically growing in the loop but that wasn't your intent)
After that, you need to create each masterList[i] :
var masterList = new Array(memlen);
for (var i=0; i < memlen; i++){
masterList[i] = {};
masterList[i].name = mem[i]; //set .name to the members list name at i
If you don't do it, it's undefined, hence the error you have.
I also added var in the loop declaration to avoid the accidental erasing of another variable named i.
A correct version would be:
var mem = getMembers();//get a array of members
var memlen = mem.length;
var masterList = []; //set to number of members
for(var i=0; i < memlen; i++){
masterList[i] = {
name: mem[i], //set .name to the members list name at i
bugs: 0,
enhance: 0,
epic: 0,
dev: 0,
high: 0
};
}
var x = [7]; does not create a 7-element array. it creates a single element array, index 0, with value 7. You need to do var x = array(7) instead, plus initialize each of those elements to be an object instead, within your loop.
This doesn't set a length. It just creates a new Array with a single member set to the length.
var masterList = [memlen]; //set to number of members
Since you seem to already have an Array, you can use .map to build a new one.
var mem = getMembers();
var masterList = mem.map(function(item) {
return {
name: item.name, bugs: 0, enhance: 0, epic: 0, dev: 0, high: 0
};
});