AMPscript IF Statement returning variable as empty - salesforce-marketing-cloud

I am running into an issue in AMPscript where the following IF statement is returning my variable #shippingVendor as nothing when I call it. (Tracking Number is from data extension)
Assuming #trackingNumber = 23456
%%[
var #shippingVendor
IF Substring(#trackingNumber, 1, 1) == "2" THEN
SET #shippingVendor = "https://www.nzpost.co.nz/tools/tracking/item/"
ELSEIF Substring(#trackingNumber, 1, 2) == "T1" THEN
SET #shippingVendor = "https://www.mainfreight.com/en-nz/tracking?trackingnumber="
ELSEIF Substring(#trackingNumber, 1, 3) == "TP1" THEN
SET #shippingVendor = "https://www.mytoll.com/?"
ENDIF
]%%
I would expect %%=v(#shippingVendor)=%% to return https://www.nzpost.co.nz/tools/tracking/item/ - however instead it returns nothing...can I please check if I am doing anything wrong?
Fairly new to AMPscript so would love your help on this - thanks in advance!

It's dangerous to do substring in AMPscript without checking for existence and length. I'd also set a default value for the shipping vendor URL.
%%[
set #trackingNumber = 23456
set #shippingVendor = "DEFAULVENDORURLHERE"
if not empty(#trackingNumber) then
IF length(#trackingNumber) >= 1 and Substring(#trackingNumber, 1, 1) == "2" THEN
SET #shippingVendor = "https://www.nzpost.co.nz/tools/tracking/item/"
ELSEIF length(#trackingNumber) >= 2 and Substring(#trackingNumber, 1, 2) == "T1" THEN
SET #shippingVendor = "https://www.mainfreight.com/en-nz/tracking?trackingnumber="
ELSEIF length(#trackingNumber) >= 3 and Substring(#trackingNumber, 1, 3) == "TP1" THEN
SET #shippingVendor = "https://www.mytoll.com/?"
ENDIF
endif
]%%
shippingVendor: %%=v(#shippingVendor)=%%
Output:
shippingVendor: https://www.nzpost.co.nz/tools/tracking/item/
Demo: https://mcsnippets.herokuapp.com/s/eiSqyIcb

Related

table returning nil value when calling function

I'm trying to pass a table through several functions and return it, but it only works to a certain degree. I'm almost sure it has to do something with the scoping, but I can't work it out since I'm new with LUA.
I tried putting the table in line 1 and setting it global, but to no avail.
Error: bag argument: expected table but got nil.
function returnToTunnel(movementTable)
for i = table.maxn(movementTable), 1, -1 do --this is where I get the error.
if (movementTable[i] == 1) then
turtle.down()
elseif (movementTable[i] == 2) then
turtle.up()
elseif (movementTable[i] == 3) then
turtle.back()
turtle.turnRight()
elseif (movementTable[i] == 4) then
turtle.back()
turtle.turnLeft()
elseif (movementTable[i] == 5) then
turtle.back()
end
end
end
function mineOre(locationParam, movementTable)
if (locationParam == 1) then
turtle.digUp()
turtle.suckUp()
turtle.up()
table.insert(movementTable, 1)
elseif (locationParam == 2) then
turtle.digDown()
turtle.suckDown()
turtle.down()
table.insert(movementTable, 2)
elseif (locationParam == 3) then
turtle.turnLeft()
turtle.dig()
turtle.suck()
turtle.forward()
table.insert(movementTable, 3)
elseif (locationParam == 4) then
turtle.turnRight()
turtle.dig()
turtle.suck()
turtle.forward()
table.insert(movementTable, 4)
elseif (locationParam == 5) then
turtle.dig()
turtle.suck()
turtle.forward()
table.insert(movementTable, 5)
end
locationParam = oreCheck()
if (locationParam > 0) then
mineOre(locationParam, movementTable)
else
return movementTable
end
end
function digTunnel(tunnelLengthParam)
local oreFound
local movement = {}
for i = 1, tunnelLengthParam do
turtle.dig()
turtle.forward()
oreFound = oreCheck()
if (oreFound > 0) then
movement = mineOre(oreFound, movement)
returnToTunnel(movement)
end
if ((i % 2) == 1) then
turtle.digUp()
turtle.up()
elseif ((i % 2) == 0) then
turtle.digDown()
turtle.down()
end
oreFound = oreCheck()
if (oreFound > 0) then
movement = mineOre(oreFound, movement)
returnToTunnel(movement)
end
end
end
So, the digTunnel function calls the other two functions mineOre and returnToTunnel.
I've been looking in the LUA manual and several websites but can't figure it out.
Thank you for your help!
Your function mineOre does not return a table but nil, when locationParam is > 0.
if (locationParam > 0) then
mineOre(locationParam, movementTable)
else
return movementTable
end
Hence this will cause a nil value end up in table.maxn
movement = mineOre(oreFound, movement)
returnToTunnel(movement)

Lua nested Json, remove single occurs or list of occurs if multiple

So what I am trying to do here is for a given json_body which is decoded json into a table using cjson I want to remove a given element by a configurable value conf.remove.json, I feel I am pretty close but its still not working, and is there a better way? Is there a safe way to find the tables "depth" and then reach out like conf.remove.json= I.want.to.remove.this creates the behavior json_table[I][want][to][remove][this] = nil without throwing some kind of NPE?
local configRemovePath= {}
local configRemoveDepth= 0
local recursiveCounter = 1
local function splitString(inputstr)
sep = "%." --Split on .
configRemovePath={}
configRemoveDepth=0
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
configRemovePath[configRemoveDepth + 1] = str
configRemoveDepth = configRemoveDepth + 1
end
end
local function recursiveSearchAndNullify(jsonTable)
for key, value in pairs(jsonTable) do --unordered search
-- First iteration
--Sample Json below, where conf.remove.json = data.id and nothing happened. conf.remove.json=data.id
--{
--"data": {
-- "d": 2,
-- "id": 1
--}
--}
-- value = {"d": 2, "id": 1}, key = "data", configRemovePath[recursiveCounter] = "data" , configRemovePath ['data','id'] , configRemoveDepth = 2
if(type(value) == "table" and value == configRemovePath[recursiveCounter] and recursiveCounter < configRemoveDepth) then --If the type is table, the current table is one we need to dive into, and we have not exceeded the configurations remove depth level
recursiveCounter = recursiveCounter + 1
jsonTable = recursiveSearchAndNullify(value)
else
if(key == configRemovePath[recursiveCounter] and recursiveCounter == configRemoveDepth) then --We are at the depth to remove and the key matches then we delete.
for key in pairs (jsonTable) do --Remove all occurances of said element
jsonTable[key] = nil
end
end
end
end
return jsonTable
end
for _, name in iter(conf.remove.json) do
splitString(name)
if(configRemoveDepth == 0) then
for name in pairs (json_body) do
json_body[name] = nil
end
else
recursiveCounter = 1 --Reset to 1 for each for call
json_body = recursiveSearchAndNullify(json_body)
end
end
Thanks to any who assist, this is my first day with Lua so I am pretty newb.
This is the official answer, found a better way with the help of Christian Sciberras!
local json_body_test_one = {data = { id = {"a", "b"},d = "2" }} --decoded json w cjson
local json_body_test_two = {data = { { id = "a", d = "1" }, { id = "b", d = "2" } } }
local config_json_remove = "data.id"
local function dump(o) --Method to print test tables for debugging
if type(o) == 'table' then
local s = '{ '
for k,v in pairs(o) do
if type(k) ~= 'number' then k = '"'..k..'"' end
s = s .. '['..k..'] = ' .. dump(v) .. ','
end
return s .. '} '
else
return tostring(o)
end
end
local function splitstring(inputstr, sep)
if sep == nil then
sep = "%." --Dot notation default
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
local function setjsonprop(json_object, path, newvalue)
local configarray = splitstring(path)
while (#configarray > 1) do
json_object = json_object[table.remove(configarray, 1)]
if(type(json_object) == "table" and #json_object > 0) then
local recursepath = table.concat(configarray, ".")
for _, item in pairs(json_object) do
setjsonprop(item, recursepath, newvalue)
end
return
end
end
json_object[table.remove(configarray, 1)] = newvalue
end
setjsonprop(json_body_test_one, config_json_remove, nil)
print(dump(json_body_test_one))

Sort lua table based on nested json value

We have a key-value pair in redis consisting of a key with a JSON object as a value with various information;
"node:service:i-01fe0d69c343734" :
"{\"port\":\"32781\",
\"version\":\"3.0.2\",
\"host-instance-id\":\"i-01fe0d69c2243b366\",
\"last-checkin\":\"1492702508\",
\"addr\":\"10.0.0.0\",
\"host-instance-type\":\"m3.large\"}"
Is it possible to sort the table based on the last-checkin time of the value?
Here is my solution to your problem, using the quick sort algorithm, before doing a little correction of your input (as I understood it):
-----------------------------------------------------
local json = require("json")
function quicksort(t, sortname, start, endi)
start, endi = start or 1, endi or #t
sortname = sortname or 1
if(endi - start < 1) then return t end
local pivot = start
for i = start + 1, endi do
if t[i][sortname] <= t[pivot][sortname] then
local temp = t[pivot + 1]
t[pivot + 1] = t[pivot]
if(i == pivot + 1) then
t[pivot] = temp
else
t[pivot] = t[i]
t[i] = temp
end
pivot = pivot + 1
end
end
t = quicksort(t, sortname, start, pivot - 1)
return quicksort(t, sortname, pivot + 1, endi)
end
---------------------------------------------------------
-- I manually added delimeter ","
-- and name "node:service..." must be different
str = [[
{
"node:service:i-01fe0d69c343731" :
"{\"port\":\"32781\",
\"version\":\"3.0.2\",
\"host-instance-id\":\"i-01fe0d69c2243b366\",
\"last-checkin\":\"1492702506\",
\"addr\":\"10.0.0.0\",
\"host-instance-type\":\"m3.large\"}"
,
"node:service:i-01fe0d69c343732" :
"{\"port\":\"32781\",
\"version\":\"3.0.2\",
\"host-instance-id\":\"i-01fe0d69c2243b366\",
\"last-checkin\":\"1492702508\",
\"addr\":\"10.0.0.0\",
\"host-instance-type\":\"m3.large\"}"
,
"node:service:i-01fe0d69c343733" :
"{\"port\":\"32781\",
\"version\":\"3.0.2\",
\"host-instance-id\":\"i-01fe0d69c2243b366\",
\"last-checkin\":\"1492702507\",
\"addr\":\"10.0.0.0\",
\"host-instance-type\":\"m3.large\"}"
,
"node:service:i-01fe0d69c343734" :
"{\"port\":\"32781\",
\"version\":\"3.0.2\",
\"host-instance-id\":\"i-01fe0d69c2243b366\",
\"last-checkin\":\"1492702501\",
\"addr\":\"10.0.0.0\",
\"host-instance-type\":\"m3.large\"}"
}
]]
-- remove unnecessary \
str = str:gsub('"{','{'):gsub('}"','}'):gsub('\\"','"')
local t_res= json.decode(str)
-- prepare table before sorting
local t_indexed = {}
for k,v in pairs(t_res) do
v["node-service"] = k
t_indexed[#t_indexed+1] = v
end
-- algoritm quicksort realised only for indexed table
local t_sort= quicksort(t_indexed, "last-checkin")
for k,v in pairs(t_sort) do
print( k , v["node-service"] , v["port"], v["version"], v["host-instance-id"], v["last-checkin"] , v["addr"], v["host-instance-type"] )
end
console:
1 node:service:i-01fe0d69c343734 32781 3.0.2 i-01fe0d69c2243b366 1492702501 10.0.0.0 m3.large
2 node:service:i-01fe0d69c343731 32781 3.0.2 i-01fe0d69c2243b366 1492702506 10.0.0.0 m3.large
3 node:service:i-01fe0d69c343733 32781 3.0.2 i-01fe0d69c2243b366 1492702507 10.0.0.0 m3.large
4 node:service:i-01fe0d69c343732 32781 3.0.2 i-01fe0d69c2243b366 1492702508 10.0.0.0 m3.large

What the heck is happening with this Linq-to-SQL query?

[Resolved - see update]
I have this L2S query (looks scary, but the bit that's going wrong is simple):
var historicDataPointValues = from dpv in this._dataContext.DataPointValues
where (dpv.categoryId == historicCategoryId)
&& (dpv.retailerId == historicRetailerId)
&& (dpv.questionId == question.PreviousQuestionId)
&& historicResponseIds.Contains(dpv.responseIndex)
&& ((dpv.dataPointIndex == firstDataPointIndex) || (dpv.dataPointIndex == secondDataPointIndex))
select new KeyValuePair<string, double>(MakeDataPointValueKey(dpv), dpv.value);
...which was producing no results, so I looked at the SQL emitted by L2S.
When I execute it, the value of firstDataPointIndex is 1 and secondDataPointIndex is null. Yet the query that L2S produces (as shown in the debugger) is:
SELECT [t0].[categoryId], [t0].[retailerId], [t0].[questionId], [t0].[responseIndex], [t0].[dataPointIndex], [t0].[value]
FROM [dbo].[DataPointValue] AS [t0]
WHERE ([t0].[categoryId] = 1)
AND ([t0].[retailerId] = 1)
AND (([t0].[questionId]) = 8)
AND ([t0].[responseIndex] IN (1, 3, 4, 5, 6, 7, 8, 10, 11, 12))
AND (([t0].[dataPointIndex] = 13) OR ((CONVERT(Int,[t0].[dataPointIndex])) = 14))
Note the values 13 & 14 where firstDataPointIndex and secondDataPointIndex should be (they should be 1 and NULL; if I put in the correct values, then I get the results I expect).
WTF?
UPDATE: Turns out that the L2S debugger was at fault. It wasn't showing the same SQL as what's actually sent to the server, which is:
exec sp_executesql N'SELECT [t0].[categoryId], [t0].[retailerId], [t0].[questionId], [t0].[responseIndex], [t0].[dataPointIndex], [t0].[value]
FROM [dbo].[DataPointValue] AS [t0]
WHERE ([t0].[categoryId] = #p0) AND ([t0].[retailerId] = #p1) AND (([t0].[questionId]) = #p2) AND ([t0].[responseIndex] IN (#p3, #p4, #p5, #p6, #p7, #p8, #p9, #p10, #p11, #p12)) AND (([t0].[dataPointIndex] = #p13) OR ((CONVERT(Int,[t0].[dataPointIndex])) = #p14))',N'#p0 int,#p1 int,#p2 int,#p3 int,#p4 int,#p5 int,#p6 int,#p7 int,#p8 int,#p9 int,#p10 int,#p11 int,#p12 int,#p13 int,#p14 int',#p0=1,#p1=1,#p2=8,#p3=1,#p4=3,#p5=4,#p6=5,#p7=6,#p8=7,#p9=8,#p10=9,#p11=10,#p12=11,#p13=1,#p14=NULL
Note that the query now contains #p13 and #p14 where previously it had scalar values.

Does mysql have function for returning a number with ordinal suffix?

Basically I'm looking for something like
SELECT ordinal(my_number) FROM my_table
which would return
1st
11th
1071st
...
etc
but preferrably without the use of a stored procedure
I don't know of a built-in function but it's pretty easy to write:
SELECT
CONCAT(my_number, CASE
WHEN my_number%100 BETWEEN 11 AND 13 THEN "th"
WHEN my_number%10 = 1 THEN "st"
WHEN my_number%10 = 2 THEN "nd"
WHEN my_number%10 = 3 THEN "rd"
ELSE "th"
END)
FROM my_table;
mysql doesn't have support for this. You'll have to handle the strings in whichever language you are getting the mysql data from.
Based on Ken's code, a custom MySQL function would be as follows:
DELIMITER $$
CREATE FUNCTION ordinal(number BIGINT)
RETURNS VARCHAR(64)
DETERMINISTIC
BEGIN
DECLARE ord VARCHAR(64);
SET ord = (SELECT CONCAT(number, CASE
WHEN number%100 BETWEEN 11 AND 13 THEN "th"
WHEN number%10 = 1 THEN "st"
WHEN number%10 = 2 THEN "nd"
WHEN number%10 = 3 THEN "rd"
ELSE "th"
END));
RETURN ord;
END$$
DELIMITER ;
Then it can be used as:
SELECT ordinal(1) -- 1st
SELECT ordinal(11) -- 11th
SELECT ordinal(21) -- 21st
SELECT ordinal(my_number) FROM my_table
It is possible in MySQL using the string functions but it gets messy real fast. You'd better just do the suffix in the language you're using. For example, in PHP you could do something like this:
function ordSuffix($num) {
if(empty($num) || !is_numeric($num) || $num == 0) return $num;
$lastNum = substr($num, -1);
$suffix = 'th';
if($lastNum == 1 && $num != 11) { $suffix = 'st'; }
elseif($lastNum == 2 && $num != 12) { $suffix = 'nd'; }
elseif($lastNum == 3 && $num != 13) { $suffix = 'rd'; }
return $num.$suffix;
}
echo ordSuffix(4); // 4th
echo ordSuffix(1); // 1st
echo ordSuffix(12); // 12th
echo ordSuffix(1052); // 1052nd
I found a way that works for me but its a bit of a hack
DATE_FORMAT(CONCAT('2010-01-', my_number), '%D')
That works because currently the number I'm looking at never gets above 25. But it doesn't generalize well so someone might be entertained by this:
CONCAT(
IF(my_number % 100 BETWEEN 11 AND 13,
FLOOR(my_number / 100),
FLOOR(my_number / 10)),
DATE_FORMAT(
CONCAT('2010-01-',
IF(my_number % 100 BETWEEN 11 AND 13
my_number % 100,
my_number % 10)),
'%D'))
But that's a lot of work just to get at the DATE_FORMAT functionality when Ken's code is simpler.