I'm trying to get the last element of an array in a velocity template dropped before joining it together into a string and showing the result in the "className": key below:
#set($elem = '"System.NotImplementedException: Test Exception')
#set($trace = $elem.replace('"',""))
#set($tracearray = $trace.split("\."))
#set($arraysize = $tracearray.size())
#set($lastelem = $tracearray.size() - 1)
{
"className":$tracearray.remove($lastelem).toString(),
"method":"$tracearray[$lastelem]"
}#if($foreach.hasNext),#end
#end
]
I've tried several different ways to get the array to drop the element and join it together into a string but haven't had any luck so far.
From the above example I'm looking for the following output to be achieved.
{
"className":"System",
"method":"NotImplementedException: Test Exception"
}
The $elem variable will be holding strings of various lengths and with a different number of .'s in them to split on so the lengths of the arrays will vary.
If you only need to remove the last element, why bother splitting the whole string? You could just do some parsing to extract the class name:
#set($elem = '"System.NotImplementedException: Test Exception')
#set($trace = $elem.replace('"',""))
#set($dot = $trace.lastIndexOf('.'))
#set($className = $trace.substring(0, $dot))
#set($method = $trace.substring($dot + 1))
{
"className": "$className",
"method": "$method"
}
Or, to accomodate the fact that the message at the end could contain a dot:
#set($elem = '"System.NotImplementedException: Test Exception')
#set($trace = $elem.replace('"',""))
#set($colon = $trace.indexOf(':'))
#set($dot = $trace.lastIndexOf('.', $colon))
#set($className = $trace.substring(0, $dot))
#set($method = $trace.substring($dot + 1))
{
"className": "$className",
"method": "$method"
}
With the method you have chosen, you would need another tool to join back the array elements with '.'. All this said, if you happen to be able to populate your Velocity context with a custom tool, all this stuff would be more easily done from with this custom tool.
Related
I am using AWS IoT to read and publish some information from a microelectronic's sensors. I am able to get the json file in the app as a big wall of text, but it is not very readable. I am using TextView as I don't want the user to be able to change the information. My problem is that I can't find a way to remove the parts that are not necessary to the user.
I am new to app creation, so I am trying to do this as simply as possible. I have my xml ready to receive the information; I just need the info in a variable that I can pass to it. I have tried implementing a character array, but haven't been able to get past the fact that I can't edit the text view.
tvLastMessage = (TextView) findViewById(R.id.tvLastMessage);
This is currently the block of text that I am receiving. It looks like this:
{"sensors":[{"name":"steamTemp","data":"181.39","state":1,
{"name":"waterTemp","data":"-713.15","state":0,
{"name":"waterFlow","data":"0.00","state":3,
{"name":"dieselFlow","data":"0.00","state":2,
{"name":"manualResetLevel","data":"1","state":0,
{"name":"waterFeederLevel","data":"1","state":0,
{"name":"autoResetPressure","data":"1","state":0,
{"name":"manualResetPressure","data":"1","state":0},
{"name":"tempLimit","data":"1","state":0,
{"name":"heatEff","data":"0.00","state":2}]}
The text does not look as formatted as this, but it is more understandable and readable to represent it this way. It normally does not have the new lines, so it will just naturally go to one as it runs out of room.
I am hoping that I can get it to simply show the numbers associated with each "name" and "data" as I have those hard-coded into my xml since they don't change. Just getting those numbers into different variables would be ideal, so I can simply reference the variable in my xml file. However, if there is a better way to do this, I am happy to take suggestions!
Try using a loop to find each instance of "name", "data", and "state", and get the values between them.
Assuming input is a string:
String string = "{\"sensors\":[{\"name\":\"steamTemp\",\"data\":\"181.39\",\"state\":1,\n{\"name\":\"waterTemp\",\"data\":\"-713.15\",\"state\":0,\n{\"name\":\"waterFlow\",\"data\":\"0.00\",\"state\":3,\n{\"name\":\"dieselFlow\",\"data\":\"0.00\",\"state\":2,\n{\"name\":\"manualResetLevel\",\"data\":\"1\",\"state\":0,\n{\"name\":\"waterFeederLevel\",\"data\":\"1\",\"state\":0,\n{\"name\":\"autoResetPressure\",\"data\":\"1\",\"state\":0,\n{\"name\":\"manualResetPressure\",\"data\":\"1\",\"state\":0},\n{\"name\":\"tempLimit\",\"data\":\"1\",\"state\":0,\n{\"name\":\"heatEff\",\"data\":\"0.00\",\"state\":2}]}";
Map<String, Double> map = new HashMap<>();
int index0 = 0, index1, index2;
while (true) {
// Get indices
index0 = string.indexOf("name", index0);
index1 = string.indexOf("data", index0);
index2 = string.indexOf("state", index1);
// If "name", "data", or "state" was not found
if (index0 == -1 || index1 == -1 || index2 == -1)
break;
// Get the data from the string and put it into the map
String key = string.substring(index0 + 7, index1 - 3);
Double value = Double.parseDouble(string.substring(index1 + 7, index2 - 3));
map.put(key, value);
// Update index
index0 = index2;
}
System.out.println(map);
Output:
{waterFlow=0.0, manualResetLevel=1.0, waterFeederLevel=1.0, manualResetPressure=1.0, waterTemp=-713.15, autoResetPressure=1.0, tempLimit=1.0, dieselFlow=0.0, heatEff=0.0, steamTemp=181.39}
I'm a bit of a newbie to programming. :P
I'm working with Processing right now to create a table of subjects with their ID, Title and Availability.
I have a 2D array that contains information like this:
units[0][0] = "CAKE100"; //Subject ID
units[1][0] = "Eating Cake And Baking Too"; //Subject Title
units[2][0] = "November"; //Subject Availability
units[0][1] = "TACO204"; //Subject ID
units[1][1] = "Tacos And Other Delicious Things"; //Subject Title
units[2][1] = "April"; //Subject Availability
units[0][2] = "KITC102"; //Subject ID
units[1][2] = "Kitchen Safety"; //Subject Title
units[2][2] = "June"; //Subject Availability
I'm trying to filter through the unit[0][x] section to find the index location of every Subject ID that has "1" in the fourth position of the string.
For example, I want to return [0] [0] and [0] [2], because "CAKE100" and "KITC102" both have "1" in the fourth position.
I've tried to use indexOf or .substring but for some reason I can't figure it out.
EDIT:
Not sure how much help it will be but here's my butchered code:
void checkLevel100() {
for ( int j = 0; j < units.length; j++) {
position = 0;
// position = units[0][j].indexOf("1"); //This returns 4;
if (units[0][j].substring(4) == "1") { //This doesn't run at all, and so it returns 0.
position = j;
}
fill(0);
text(position, width/2, height/2);
}
}
I also did what Kevin Workman suggested. Here is the code for that:
for (int i = 0; i < units.length; i++) {
if(units[0][i] == "TACO204"){ //This results in 1, as expected
location[i] = i;
println(i);
}
Once again, thank you for your time :)
You need to break your problem down into smaller steps.
Step 1: Loop over your 2D array. You might use a nested for loop for this.
To test that this step works, you might just print every element in your 2d array before worrying about any logic.
Step 2: Write an if statement that checks whether the value at that index passes your test.
To test that this step works, you might want to create a separate program that just tests a hardcoded value instead of using arrays.
Step 3: Once you have the first two steps working, then you can save the indexes that pass your test into some kind of data structure. You might us an ArrayList<Integer> for this.
Create a separate example program that just loops over an array without worrying about any logic. Create another separate program that just uses an if statement to test your condition against a hard-coded value. Then if you get stuck on a specific step, you can post a more specific question along with an MCVE. Good luck.
I think it would be simpler for you to store your subject's values as an object of a class. Perhaps store the numerical portion of the ID as an int too? Using a class will enable you to write more human readable code.
public class FoodSubject {
String stringID;
int numID;
String title;
String availability;
public FoodSubject(String stringID, int numID,
String title, String availability) {
this.stringID = stringID;
this.numID = numID;
this.title = title;
this.availability = availability;
}
}
To make a new array of your FoodSubject objects use this code:
FoodSubject[] subjectArray = new FoodSubject[3];
subjectArray[0] = new FoodSubject("CAKE", 100, "Eating Cake And Baking Too", "November");
Access the values like this subjectArray[0].numID
Look into overriding the toString() method in your class to print out all the values of your subject easily. This will help you identify problems in the rest of your code much easier!
If you still want to store the entire ID as a String use the charAt(int index) function to test if the character is a '1'.
This if (units[0][j].substring(4) == "1") { code is incorrect. Look at the String Java documentation for information on what substring does.
I want to query multiple candidates for a search string which could look like "My sear foo".
Now I want to look for documents which have a field that contains one (or more) of the entered strings (seen as splitted by whitespaces).
I found some code which allows me to do a search by pattern:
#View(name = "find_by_serial_pattern", map = "function(doc) { var i; if(doc.serialNumber) { for(i=0; i < doc.serialNumber.length; i+=1) { emit(doc.serialNumber.slice(i), doc);}}}")
public List<DeviceEntityCouch> findBySerialPattern(String serialNumber) {
String trim = serialNumber.trim();
if (StringUtils.isEmpty(trim)) {
return new ArrayList<>();
}
ViewQuery viewQuery = createQuery("find_by_serial_pattern").startKey(trim).endKey(trim + "\u9999");
return db.queryView(viewQuery, DeviceEntityCouch.class);
}
which works quite nice for looking just for one pattern. But how do I have to modify my code to get a multiple contains on doc.serialNumber?
EDIT:
This is the current workaround, but there must be a better way i guess.
Also there is only an OR logic. So an entry fits term1 or term2 to be in the list.
#View(name = "find_by_serial_pattern", map = "function(doc) { var i; if(doc.serialNumber) { for(i=0; i < doc.serialNumber.length; i+=1) { emit(doc.serialNumber.slice(i), doc);}}}")
public List<DeviceEntityCouch> findBySerialPattern(String serialNumber) {
String trim = serialNumber.trim();
if (StringUtils.isEmpty(trim)) {
return new ArrayList<>();
}
String[] split = trim.split(" ");
List<DeviceEntityCouch> list = new ArrayList<>();
for (String s : split) {
ViewQuery viewQuery = createQuery("find_by_serial_pattern").startKey(s).endKey(s + "\u9999");
list.addAll(db.queryView(viewQuery, DeviceEntityCouch.class));
}
return list;
}
Looks like you are implementing a full text search here. That's not going to be very efficient in CouchDB (I guess same applies to other databases).
Correct me if I am wrong but from looking at your code looks like you are trying to search a list of serial numbers for a pattern. CouchDB (or any other database) is quite efficient if you can somehow index the data you will be searching for.
Otherwise you must fetch every single record and perform a string comparison on it.
The only way I can think of to optimize this in CouchDB would be the something like the following (with assumptions):
Your serial numbers are not very long (say 20 chars?)
You force the search to be always 5 characters
Generate view that emits every single 5 char long substring from your serial number - more or less this (could be optimized and not sure if I got the in):
...
for (var i = 0; doc.serialNo.length > 5 && i < doc.serialNo.length - 5; i++) {
emit([doc.serialNo.substring(i, i + 5), doc._id]);
}
...
Use _count reduce function
Now the following url:
http://localhost:5984/test/_design/serial/_view/complex-key?startkey=["01234"]&endkey=["01234",{}]&group=true
Will return a list of documents with a hit count for a key of 01234.
If you don't group and set the reduce option to be false, you will get a list of all matches, including duplicates if a single doc has multiple hits.
Refer to http://ryankirkman.com/2011/03/30/advanced-filtering-with-couchdb-views.html for the information about complex keys lookups.
I am not sure how efficient couchdb is in terms of updating that view. It depends on how many records you will have and how many new entries appear between view is being queried (I understand couchdb rebuilds the view's b-tree on demand).
I have generated a view like that that splits doc ids into 5 char long keys. Out of over 1K docs it generated over 30K results - id being 32 char long, simple maths really: (serialNo.length - searchablekey.length + 1) * docscount).
Generating the view took a while but the lookups where fast.
You could generate keys of multiple lengths, etc. All comes down to your records count vs speed of lookups.
I'm trying to output a pipe into different directories such that the output of each directory will be bucketed based on some ids.
So in a plain map reduce code I would use the MultipleOutputs class and I would do something like this in the reducer.
protected void reduce(final SomeKey key,
final Iterable<SomeValue> values,
final Context context) {
...
for (SomeValue value: values) {
String bucketId = computeBucketIdFrom(...);
multipleOutputs.write(key, value, folderName + "/" + bucketId);
...
So i guess one could do it like this in scalding
...
val somePipe = Csv(in, separator = "\t",
fields = someSchema,
skipHeader = true)
.read
for (i <- 1 until numberOfBuckets) {
somePipe
.filter('someId) {id: String => (id.hashCode % numberOfBuckets) == i}
.write(Csv(out + "/bucket" + i ,
writeHeader = true,
separator = "\t"))
}
But I feel that you would end up reding the same pipe many times and it will affect the overall performance.
Is there any other alternatives?
Thanks
Yes, of course there is a better way using TemplatedTsv.
So your code above can be written as follows,
val somePipe = Tsv(in, fields = someSchema, skipHeader = true)
.read
.write(TemplatedTsv(out, "%s", 'some_id, writeHeader = true))
This will put all records coming from 'some_id into separate folders under out/some_ids folder.
However, you can also create integer buckets. Just change the last lines,
.map('some_id -> 'bucket) { id: String => id.hashCode % numberOfBuckets }
.write(TemplatedTsv(out, "%02d", 'bucket, writeHeader = true, fields = ('all except 'bucket)))
This will create two digit folders as out/dd/. You can also check templatedTsv api here.
There might be small problem using templatedTsv, that is reducers can generate lots of small files which can be bad for the next job using your results. Therefore, it is better to sort on template fields before writing to disk. I wrote a blog about about it here.
I am trying to write a query such as this:
select {r: referrers(f), count:count(referrers(f))}
from com.a.b.myClass f
However, the output doesn't show the actual objects:
{
count = 3.0,
r = [object Object]
}
Removing the Javascript Object notation once again shows referrers normally, but they are no longer compartmentalized. Is there a way to format it inside the Object notation?
So I see that you asked this question a year ago, so I don't know if you still need the answer, but since I was searching around for something similar, I can answer this. The problem is that referrers(f) returns an enumeration and so it doesn't really translate well when you try to put it into your hashmap. I was doing a similar type of analysis where I was trying to find unique char arrays (count the unique combinations of char arrays up to the first 50 characters). What I came up with was this:
var counts = {};
filter(
map(
unique(
map(
filter(heap.objects('char[]'), "it.length > 50"), // filter out strings less than 50 chars in length
function(charArray) { // chop the string at 50 chars and then count the unique combos
var subs = charArray.toString().substr(0,50);
if (! counts[subs]) {
counts[subs] = 1;
} else {
counts[subs] = counts[subs] + 1;
}
return subs;
}
) // map
) // unique
, function(subs) { // map the strings into an array that has the string and the counts of that string
return { string: subs, count: counts[subs] };
}) // map
, "it.count > 5000"); // filter out strings that have counts < 5000
This essentially shows how to take an enumeration (heap.objects('char[]') in this case) and filter it and map it so that you can compute statistics on it. Hope this helps someone.