Mapping list to Map not working - groovy

I have a map
["name1":["field1":value1, "field2":value2, "field3":value3],
"name2":["field1":value4, "field2":value5, "field3":value6],
"name3":["field1":value7, "field2":value8, "field3":value9]]
and a list
[name1, name3]
I wanted a result as
["name1":["field1":value1, "field2":value2, "field3":value3],
"name3":["field1":value7, "field2":value8, "field3":value9]]
The code used
result = recomendationOffers.inject( [:] ) { m, v ->
if( !m[ v ] ) {
m[ v ] = []
}
m[ v ] << tariffRecMap[ v.toString() ]
m
}
Now the datatype of the name1 changed from Varchar2(35) to number(10).
I expected the same logic to work but it is not working and I am getting values
["name1":[null], "name3":[null]]
also the value such as 1000000959 is displayed as 1.000000959E9, is this making any difference ?
posting the original values
When I was handling with string, it looked as below
["FBUN-WEB-VIRGIN-10-24-08":["FIXEDLN_ALLOWANCE":0.0,
"OFFER_VERSION_ID":1.000013082E9, "OFFER_TYPE_DESC":"Contract",
"OFFER_NAME":"PM_V 10 50+250 CA", "SMS_ALLOWANCE":250.0,
"VM_TARIFF_FLAG":"N", "IPHONE_IND":"N", "OFFER_MRC":10.5,
"ALLOWANCE08":0.0, "DATA_ALLOWANCE":524288.0, "BB_IND":"N",
"CONTRACT_TERM":24.0, "OFFER_CODE":"FBUN-WEB-VIRGIN-10-24-08",
"ONNET_TEXT_ALLOWANCE":0.0, "VOICE_ALLOWANCE":50.0,
"MMS_ALLOWANCE":0.0, "ONNET_ALLOWANCE":0.0],
Now after the database datatype changed to number from varchar it looks as below where the value in DB is 1000010315
[1.000010315E9:["FIXEDLN_ALLOWANCE":0.0,
"OFFER_VERSION_ID":1.000010315E9, "OFFER_TYPE_DESC":"Sup Voice",
"OFFER_NAME":"VIP - 35 c", "SMS_ALLOWANCE":60000.0,
"VM_TARIFF_FLAG":"N", "IPHONE_IND":"N", "OFFER_MRC":35.0,
"ALLOWANCE08":45000.0, "DATA_ALLOWANCE":2.147483648E9, "BB_IND":"N",
"CONTRACT_TERM":24.0, "OFFER_CODE":"FBUN-MVP-WEB-VIRGIN-35-24-20",
"ONNET_TEXT_ALLOWANCE":0.0, "VOICE_ALLOWANCE":45000.0,
"MMS_ALLOWANCE":0.0, "ONNET_ALLOWANCE":0.0]

Now the datatype of the name1 changed from Varchar2(35) to number(10) ... also the value such as 1000000959 is displayed as 1.000000959E9, is this making any difference ?
Yes, all the difference in the world. That means you're converting a Double (most likely) to a String, and as the String "1000000959" is not equal to "1.000000959E9", you don't get a match.
Not sure from the question which bits are doubles and which bits are Strings... Maybe you could expand with an actual example?
Also, your inject method can be replaced with:
def result = tariffRecMap.subMap( recomendationOffers )

Related

Using Faker to get and set gender for all following data

I'm using Faker with TypeScript and am struggling to figure out how to capture the returned gender from Faker in a way that I can then use in subsequent calls. This is what I'd like to do:
const gender = faker.name.gender(true); // <-- returns a string
const firstName = faker.name.firstName(gender);
const middleName = faker.name.middleName(gender);
Unfortunately, Faker's faker.name.firstName(gender) takes a GenderType enum value, not a string, even though you can use it like faker.name.firstName('male'). That works, but using the variable gender doesn't work.
Am I missing something here?
faker.name.firstName has the type (gender?: GenderType) => string, where type GenderType = 'female' | 'male' | 0 | 1;*.
However, faker.name.gender has the type (binary?: boolean = false) => string. It can return quite a broad range of values:
> faker.name.gender()
'T* woman'
> faker.name.gender()
'Intersex'
> faker.name.gender()
'Cis'
Unhelpfully, even if you set binary to true, what it returns is "Female" or "Male", neither of which is actually a GenderType:
> faker.name.gender(true)
'Male'
> faker.name.gender(true)
'Female'
although there is some normalisation, so this works in JavaScript (but not TypeScript):
> faker.name.firstName("Male")
'Ilene'
To make this work you'd have to add some type assertions, for example:
import { faker, GenderType } from "#faker-js/faker";
const gender = faker.name.gender(true).toLowerCase() as GenderType;
// ^ we know this will be true
const name = faker.name.firstName(gender);
or, if you want to work with the capitalised version, using the intrinisic string manipulation types:
const gender = faker.name.gender(true) as "Female" | "Male";
// ^ we know this will be true
// ...
const name = faker.name.firstName(gender.toLowerCase() as Lowercase<typeof gender>);
// ^ this is obviously true
// (if ugly...)
These seem awkward enough that I've opened a bug to explore whether it can be fixed upstream.
(Also it looks like the string case typing is being explored in TypeScript, so maybe the as Lowercase won't be needed at some point.)
* The numerical values are deprecated:
> faker.name.firstName(0)
[#faker-js/faker]: name.firstName(number) is deprecated since v6.1.0 and will be removed in v7.0.0. Please use 'female' or 'male' instead.
'Forrest'

Implementing AT-POS to return an object instead of a list of things

I've the following class:
class Names {
has #!names;
method add-name( $name ) { #!names.push($name) }
multi method AT-POS( ::?CLASS:D: $index ) {
my $new-names-obj = Names.new;
for #!names[$index] -> $name {
$new-names-obj.add-name($name);
}
return $new-names-obj;
}
method gist {
#!names.join("\n")
}
}
I'd like to be able to slice a Names object and the returned value
should be another Names object whose elements are sliced off from the
original Names object. For instance:
my $original = Names.new;
$original.add-name($_) for <jonathan joseph jotaro josuke giorno>;
my $sliced-off = $original[0..2];
say $original.^name; #=> Names
say $original; #=> jonathan, joseph, jotaro, josuke, giorno
say $sliced-off.^name; #=> List
say $sliced-off; #=> (jonathan joseph jotaro)
When a single argument is passed, it works as expected and as described in this answer but it's not the case with range since AT-POS ends up being called multiple times and collecting the results in a list. Thus I'm wondering if it's possible to return a single object $sliced-off, not a list of results, when using a range.
The AT-POS method is intended to let an object act as a Positional object. This is not what you appear to want. You want object[slice] DWIM.
The best way to achieve that, is to create a postcircumfic:<[ ]> (multi) candidate for your object:
class A {
method slice(#_) {
say #_; # just to show the principle
}
}
sub postcircumfix:<[ ]>($object, *#indices) {
constant &slicer = &postcircumfix:<[ ]>;
$object ~~ A
?? $object.slice(#indices)
!! slicer($object, #indices)
}
A.new[1,2,4,5]; # [1 2 4 5]
my #a = ^10; # check if foo[] still works
say #a[1,2,4,5]; # (1 2 4 5)
To make sure that the common behaviour of #a[] is kept, we save the value of the system's postcircumfix:[ ]> at compile time (with a constant). Then at runtime, when the object is not of the right class, invoke the original version of postcircumfix:<[ ]> with the given parameters.
Building on Liz's guidance:
class Names {
has #.names; # Make public so [] can access.
method new (*#names) { nextwith :#names } # Positional .new constructor.
submethod BUILD (:#!names) {} # Called by nextwith'd Mu new.
multi sub postcircumfix:<[ ]> # Overload [] subscript.
( Names $n, $index, *#indices ) # Why `$index, *#indices`?
is default is export # And why `is default`?
{ Names.new: |$n.names[ |$index, |#indices ] } # Why? See my comment
method gist { #!names.join(', ') } # below Liz's answer.
}
import Names;
my $original = Names.new: <jonathan joseph jotaro josuke giorno>;
my $sliced-off = $original[0..2];
say $original.^name; #=> Names
say $original; #=> jonathan, joseph, jotaro, josuke, giorno
say $sliced-off.^name; #=> Names
say $sliced-off; #=> jonathan, joseph, jotaro
PLMK if the code or explanation is inadequate.

Update a Map in groovy spock framework

I have the below spock specification and want to update the map from data table. Can some body help achieve this
def "groovy map update"() {
setup: "step1"
Map json = [
user :[
name : 'ABC'
]]
when: "step2"
println ('Before modification:')
println (json)
then: "step3"
json.with {
//user.name = value // this one works
(field) = value // this one does not work
}
println ('After modification:')
println (json)
where:
field | value
'user.name' | 'XYZ'
}
The then section is intended for asserts and not for updates etc. So you have to update the map in the when section and then test the result in the then section. For example like this:
def "groovy map update"() {
setup: 'create json'
Map json = [user: [name: 'ABC']]
when: 'update it'
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
then: 'check the assignment'
json.user.name == value
where:
path | value
['user', 'name'] | 'XYZ'
}
One way how to update nested Map value can be by using list of path nodes instead of field notation and then iterate over them to obtain the last Map instance and set the value there:
def target = json
for (node in path - path.last()) {
target = target[node]
}
target[path.last()] = value
The accepted solution is correct, I just want to show an alternative doing the same in a slightly different way, assuming you want to stick with the dotted notation for field in your where: block. I just added two more test cases in order to make sure it works as expected.
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
def subMap = json
field.split("[.]").each {
if (subMap[it] instanceof Map)
subMap = subMap[it]
else
subMap[it] = value
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Update: If you want to save a few lines of code you can also use a fold (or reduce or accumulate) operation via inject(..) as described here
#Unroll
def "set #field to #value"() {
setup: 'create json'
Map json = [user: [name: 'ABC', address: [street: '21 Main St', zip: '12345', city: 'Hometown']]]
when: 'update it'
field.split("[.]").inject(json) { subMap, key ->
subMap[key] instanceof Map ? subMap[key] : subMap.put(key, value)
}
println json
then: 'check the assignment'
json.newField == value ||
json.user.name == value ||
json.user.address.zip == value
where:
field | value
'newField' | 'dummy'
'user.name' | 'XYZ'
'user.address.zip' | '98765'
}
Whether you find that readable or not may depend on your familiarity with topics like functional programming in general or map/reduce in particular. The charm here in addition to brevity is that we no longer need a local variable outside of our closure but we just inject (hence the method name) the result of iteration n to iteration n+1.
BTW, as a nice side effect inject(..) as I am using it here returns the previous value of the value you set or overwrite. Just add println in front of field.split("[.]").inject(json) ... in order to see it.
Update 2: Please note that both variants only work if there is no existing field value of type Map in the target field because of the instanceof Map check heuristics in my code. I.e. these two cases would not work:
'user.address' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']
'user.address' | '23 Test Blvd, 33333 Somewhere'
This one would work, though, because there is no preexisting value:
'user.alternativeAddress' | [street: '23 Test Blvd', zip: '33333', city: 'Somewhere']

using as.ppp on data frame to create marked process

I am using a data frame to create a marked point process using as.ppp function. I get an error Error: is.numeric(x) is not TRUE. The data I am using is as follows:
dput(head(pointDataUTM[,1:2]))
structure(list(POINT_X = c(439845.0069, 450018.3603, 451873.2925,
446836.5498, 445040.8974, 442060.0477), POINT_Y = c(4624464.56,
4629024.646, 4624579.758, 4636291.222, 4614853.993, 4651264.579
)), .Names = c("POINT_X", "POINT_Y"), row.names = c(NA, -6L), class = c("tbl_df",
"tbl", "data.frame"))
I can see that the first two columns are numeric, so I do not know why it is a problem.
> str(pointDataUTM)
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 5028 obs. of 31 variables:
$ POINT_X : num 439845 450018 451873 446837 445041 ...
$ POINT_Y : num 4624465 4629025 4624580 4636291 4614854 ...
Then I also checked for NA, which shows no NA
> sum(is.na(pointDataUTM$POINT_X))
[1] 0
> sum(is.na(pointDataUTM$POINT_Y))
[1] 0
When I tried even only the first two columns of the data.frame, the error I get on using as.ppp is this:
Error: is.numeric(x) is not TRUE
5.stop(sprintf(ngettext(length(r), "%s is not TRUE", "%s are not all TRUE"), ch), call. = FALSE, domain = NA)
4.stopifnot(is.numeric(x))
3.ppp(X[, 1], X[, 2], window = win, marks = marx, check = check)
2.as.ppp.data.frame(pointDataUTM[, 1:2], W = studyWindow)
1.as.ppp(pointDataUTM[, 1:2], W = studyWindow)
Could someone tell me what is the mistake here and why I get the not numeric error?
Thank you.
The critical check is whether PointDataUTM[,1] is numeric, rather than PointDataUTM$POINT_X.
Since PointDataUTM is a tbl object, and tbl is a function from the dplyr package, what is probably happening is that the subset operator for the tbl class is returning a data frame, and not a numeric vector, when a single column is extracted. Whereas the $ operator returns a numeric vector.
I suggest you convert your data to data.frame using as.data.frame() before calling as.ppp.
In the next version of spatstat we will make our code more robust against this kind of problem.
I'm on the phone, so can't check but I think it is happens because you have a tibble and not a data.frame. Please try to convert to a data.frame using as.data.frame first.

How to find the closest time value to a given time value in matlab

Say that I have a time value given, for example: 2012-03-28_15:10:00
and then I have a sting that stores multiple time values:
2012-03-28_14:00:00
2012-03-28_14:10:00
2012-03-28_14:20:00
2012-03-28_14:30:00
2012-03-28_14:40:00
2012-03-28_14:50:00
2012-03-28_15:00:00
2012-03-28_15:05:00
2012-03-28_15:20:00
2012-03-28_15:30:00
I want to find the time value in the string that is the closest to the original time value.
Does anyone know how this can be done in matlab?
Code
data1 = '2012-03-28_15:10:00'
data2 = [
'2012-03-28_14:00:00'
'2012-03-28_14:10:00'
'2012-03-28_14:20:00'
'2012-03-28_14:30:00'
'2012-03-28_14:40:00'
'2012-03-28_14:50:00'
'2012-03-28_15:00:00'
'2012-03-28_15:05:00'
'2012-03-28_15:20:00']
[~,ind1] = min(abs(datenum(data2)-datenum(data1)));
closest_time = data2(ind1,:)
Output
closest_time =
2012-03-28_15:05:00
Extended Part: If you have many dates, as a char matrix too and to be compared to the list, then using a bsxfun approach might be a better solution, as it avoids loops. This is shown below -
Code
data1 = [
'2012-03-28_14:02:00'
'2012-03-28_14:11:00'
'2012-03-28_14:23:00'
'2012-03-28_14:32:00']
data2 = [
'2012-03-28_14:00:00'
'2012-03-28_14:10:00'
'2012-03-28_14:20:00'
'2012-03-28_14:30:00'
'2012-03-28_14:40:00'
'2012-03-28_14:50:00'
'2012-03-28_15:00:00'
'2012-03-28_15:05:00'
'2012-03-28_15:08:00']
[~,ind1] = min(abs(bsxfun(#minus,datenum(data2),datenum(data1)')));
closest_time = data2(ind1,:)
Output
closest_time =
2012-03-28_14:00:00
2012-03-28_14:10:00
2012-03-28_14:20:00
2012-03-28_14:30:00

Resources