I have a multi-value parameter how I check in iif condition that value of idex is null or not
now, I am using this expression
IIF(IsNothing(Parameters!FR.Value(0))," ","Test")
My parameter is hidden and some thing it has 4 values or some time less than 4.
when I check that values of index 3 value(3) and there is no value in this index. I shows error. How I check the index value of parameter.
The first problem is that there is no object at index 3, not that the value of the object is Nothing, so you will get an error trying to access the Value property of an object that doesn't exist.
The second problem is that IIF is a function not a language construct, so all parameters are evaluated before the function is called regardless of the value of the boolean condition. I can't see a way of trying to access the Value(3) property without the IIF function blowing up in your face when it doesn't exist. You could try something convoluted like this:
=IIF(Parameters!FR.Count >= 4, Parameters!FR.Value(IIF(Parameters!FR.Count >= 4, 3, 0)), "")
but that is still going to blow up in your face when there are no parameter values selected at all.
The easiest and safest way to do it would be to create a custom code function to which you pass the multi-value parameter and the index you want:
Function Get_FR_by_Index(ByVal parameter As Parameter, ByVal index As Integer) AS String
Dim Result As String
Result = ""
If parameter.IsMultiValue Then
If parameter.Count >= index+1 Then
Result = CStr(parameter.Value(index))
End If
End If
Return Result
End Function
then use this function to access the result:
=Code.Get_FR_by_Index(Parameters!FR, 3)
Remember that the index is zero-based, so the first Value is index 0.
Related
I've got an array with 2 values in it. I want to use an If statement to see whether any of the values in the array equals the SelectedValue of my DropDown.
So far, everything I've tried only gives negative results (only the Else condition is reached):
Dim twoValues() As String = {"one", "two"}
If Me.ddlDropdown.SelectedValue.ToLower.Equals(twoValues.Any) Then
'do something
Else
'do something else
End If
You can invert the comparison and use LINQ's Any() method to compare elements of the array to the provided value, until a matching value is found.
Since the comparison needs to be case-insensitive, you can use the overload of String.Equals() that accepts a StringComparison argument.
Better avoid ToLower() without a CultureInfo selector when a case-insensitive comparison is needed.
Also, the InvariantCulture may apply or not, depending on the languages involved in the comparison.
If the current language is the local language and cannot be directly associated with the InvariantCulture, use StringComparison.CurrentCultureIgnoreCase instead. Or build a custom comparer.
Dim twoValues As String() = {"one", "two"}
Dim value = Me.ddlDropdown.SelectedValue.ToString()
If twoValues.Any(Function(s) s.Equals(value, StringComparison.InvariantCultureIgnoreCase)) Then
'do something
Else
'do something else
End If
Looking for a one liner code either in java or cfm, where i do not need to loop over te array of structs to use te structfind to get the value from it.
right now looking at it,
Coldfusion - How to loop through an Array of Structure and print out dynamically all KEY values?
where i can loop over and get the value of the key match
but trying to check if something like this can be done
<cfset myvalue = structfindvaluefromAnything(myarrayofstruct,"infor")>
I like Sev's approach. I would change it slightly
<cfscript>
superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Spider-Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
avengers = superheroes.filter(function(item) {
return item.member == "Avengers";
});
writeDump(avengers);
</cfscript>
If you really want to do it in one line then you could use ArrayFilter() in combination with StructFindValue().
Adapting from the Adobe docs for ArrayFilter - https://helpx.adobe.com/coldfusion/cfml-reference/coldfusion-functions/functions-a-b/arrayfilter.html - something like this:
<cfscript>
superheroes=[
{"name":"Iron Man","member":"Avengers"},
{"name":"Wonder Woman","member":"Justice League"},
{"name":"Hulk","member":"Avengers"},
{"name":"Thor","member":"Avengers"},
{"name":"Aquaman","member":"Justice League"}
];
avengers=ArrayFilter(superheroes,function(item){
return ArrayLen(StructFindValue( item, "Avengers"));
});
writeDump(var=avengers, label="all matches");
writeDump(var=ArrayLen(avengers) ? avengers[1] : "Not found", label="first match only");
writeDump(var=structFindValue({"a":superheroes}, "Avengers", "all"), label="without arrayFilter");
</cfscript>
I believe the function available for this nearly exactly what you were hoping for...
StructFindValue(struct, value [, scope])
Searches recursively through a substructure of nested arrays, structures, and other elements for structures with values that match the search key in the value parameter.
Returns an array that contains structures keys whose values match the search key value. If none are found, returns an array of size 0.
Based on the gist you provided above (https://cffiddle.org/app/file?filepath=3e26c1ac-d5db-482f-9bb2-995e6cabe704/49b3e106-8db9-4411-a6d4-10deb3f8cb0e/24e44eba-45ef-4744-a6e6-53395c09a344.cfm), I think you've clarified your expectations a little bit.
In your gist, you say you want to be able to search an array of structs and find the row that has a "name" key with a value of "form". Then, you want to take the value of the "value" key that's associated with that struct in the array row. If there is no value then return 0.
You wanted to be able to do this in a single line of code, and the above answers do accomplish that. My answer essentially builds on those.
As demonstrated in the earlier answers, you still want to use closure functions to filter down your final output. Those are very quick and essentially built to do what you're trying to do.
The Fiddle that I worked with is here: https://cffiddle.org/app/file?filepath=b3507f1d-6ac2-4900-baed-fb3faf5a3b3a/e526afc2-bb85-4aea-ad0e-dcf38f52b642/75d88d2b-f990-44c1-9d9f-22931bf9d4d7.cfm
I've done two things with this.
First, I worked it as if you expected to encounter multiple records for your filtering value, and then turn those into a comma-delimited list. If you need another structure, the reduce() function in my code can be modified to handle this.
Second, I worked it as if you expected to encounter only one filtered record, returning only a single value.
The first thing I did, which is mostly the same in both methods, and which is essentially the same as the previous answers, is to filter your original array for just the value you want.
This is done like this:
myResult = originalArray.filter(
function(itm){
return itm?.name=="form"; /// ?. = safe-navigation operator.
}
)
I've broken it to multiple lines for clarity.
This will return a new array of structs consisting of your filtered rows.
But then you want to take those records and return the "value" from those rows (defaulting to 0 if no value. You can do this with a reduce().
commaDelimitedValue =
myResult.reduce(
function(prev,nxt) {
return prev.listappend( ( nxt.value.len() ? nxt.value : 0 ) ) ;
}
, "" /// Initialization value
) ;
Again, this can be written in one row, but I've included line breaks for clarity.
The reduce() function essentially just reduces your input to a single value. It follows the format of .reduce( function( previousValue, nextValue ){ return .... },<initializationValue>), where, on the first iterations, the initializationValue is substituted for previousValue, then previousValue becomes the result of that iteration. nextValue is actually the current iteration that you will derive a result from.
More at: https://coldfusion.adobe.com/2017/10/map-reduce-and-filter-functions-in-coldfusion/
In my assumption here, you could possibly have multiple rows returned from your filter(). You take those rows and append the value to a commma-delimited list. So you would end up with a result like 20,10,0,0 - representing 4 rows in your filtered results.
I also check for a length of the value and default it to 0 if it's an empty string. Above, I said that you could just use an Elvis Operator (:?) on that, but that doesn't work for a simple value like an empty string. Elvis works with NULLs, which the earlier array did have.
To put this back to one line, you can chain both of these functions. So you end up with:
myFinalResult =
myOriginalArray.filter(
function(itm){
return itm?.name=="form";
}
)
.reduce(
function(prev,nxt) {
return prev.listappend( ( nxt.value.trim().len() ? nxt.value : 0 ) ) ;
}
, ""
)
;
Again, that code is doing a lot, but it is still essentially one line. The final result from that would again be something like "20,10,0,0" for 4 rows with 2 defaulted to 0.
If you only expect your filter to return a single row, or if you only want a single value, you can simplify that a little bit.
myFinalResult = myOriginalArray.filter( function(itm){ return itm?.name=="fm" && (itm?.value.trim().len()>0) ; } )[1]["value"] ?: 0 ;
With this, I am back to using my previous trick with Elvis to default a row with no value, since I am filtering out the "form" struct with an empty-string "value". && is the same as AND. Technically this CAN filter more than one row from the original array, but the [1] will only pick the first row from the filtered rows. It also doesn't need to use a reduce(). If there's more than one row filtered, each iteration will just overwrite the previous one.
This will return a simple, single value with something like 42 - which is the last filtered value in the array, since it overwrites the previous row's value.
My Fiddle (https://cffiddle.org/app/file?filepath=b3507f1d-6ac2-4900-baed-fb3faf5a3b3a/e526afc2-bb85-4aea-ad0e-dcf38f52b642/75d88d2b-f990-44c1-9d9f-22931bf9d4d7.cfm) has some additional comments, and I set up a couple of edge cases that demonstrate the filtering and safe-navigation.
I would also like to reiterate that if this is Lucee 5+ or ACF2018+, you can shorten this further with Arrow Functions.
I have have a sub which creates multiple empty arrays, resizes them, loops a large set of data and then calls a function to apply a value to one of the arrays.
The function which updates the arrays is called numerous times within the procedure, yet I am currently using multiple if statements to determine which array should be passed as a parameter. Therefore, I am trying to work out how I can pass the name of the array using either another function - which returns the name of the array, or a dictionary, where i can supply the key and the array name as the item.
For the function idea:
Call updateArray(getArrayName(criteria))
function getArrayName(byVal criteria as String) as String
if criteria = "a" then getArrayName = "array1"
...
if criteria = "z" then getArrayName = "array26"
End function
The calling sub does not recognize the value as a name of a variable, it just thinks it is a string, ie "array1" which then causes a debug, as a array is not actually passed into the updateArray function.
For the dictionary idea:
dim myDict as new scripting.dictionary
dict.add "a", array1
...
dict.add "z", array26
dim arrayKey as string
arrayKey = criteria
call myFunction(myDict(arrayKey))
when I pass the array name using the key/item to the function, an array is passed in with the correct dimensions (set earlier in the code) as i can use a debug.print to get the lbound and ubound values. However, once the function completes, the expected value has not updated in any array. When looking in the watch window during runtime, if I locate the array I am expecting to have been passed in, the value in the expected position does not update.
Any ideas how I can achieve this?
update: as suggested, here is the code for myFunction.
I was keeping the above code quite simple to help explain my issue, as you can see, the real code requires more parameters.
A dictionary (which is created when sizing each array) that contains all the row and column positions of the array (the row number is accessed via the account var, the column number is accessed via the oppType var). There are numerous oppType categories in the main data set, however in the output array, some categories store either an FTE value, a USD value or both. Therefore, the catType var is also passed in to allow me to correctly identify the correct column. e.g. the oppType could be "CC" the output data has 2 columns for this category "CC" and "CC USD". The fte value goes in the "CC" col, the $USD value goes in the "CC USD" col.
The loop in the main sub determines if the value is an FTE or USD value (boValue). The qData variant is the actual array that is passed in and updated.
Function updateLosArray(ByRef arrayPosDict As Dictionary, ByRef qData As Variant, _
ByVal account As String, ByVal oppType As String, ByVal boValue As Double, ByVal catType As String)
Dim accRow As Long, oppTypeCol As Long
'calculate the correct array row position
accRow = arrayPosDict(account)
'calculate correct array column position
If catType = "FTE" Then oppTypeCol = arrayPosDict(oppType)
If catType = "USD" Then oppTypeCol = arrayPosDict(oppType & " USD")
'update position value
qData(accRow, oppTypeCol) = qData(accRow, oppTypeCol) + boValue
End Function
As mentioned, at the moment, I use multiple If statements that call the function. This is an example for an FTE item:
If qtr = "Q1" Then updateLosArray arrayPosDict, q1Data, account, oppType, fte, "FTE"
If qtr = "Q2" Then updateLosArray arrayPosDict, q2Data, account, oppType, fte, "FTE"
...
If qtr = "Q100" Then updateLosArray arrayPosDict, q100Data, account, oppType, fte, "FTE"
my idea was, instead of having multiple Ifs, I could just have one line of code to call the function, so replace the hard coded array name with a variable that gets the correct name, eg using a dictionary where the qtr var is the key, and the q?Data array is the item.
e.g.
updateLosArray arrayPosDict, arrayNameDict(qtr), account, oppType, fte, "FTE"
Sorry this is quite long winded!
Thanks
Given an array, I would like to have a function which determines if a string of characters exists in an array.
The string of characters must be EXACTLY the same in input of the function, and the array content.
Function IsInArray(ThsStrng As String, arr() As String, bnd As Integer) As Boolean
For Z = 1 To bnd
If arr(Z) = ThsStrng Then
IsInArray = True
Else
IsInArray = False
End If
Next Z
End Function
At first, it seemed like the function ran correctly. After using this function a few times, I noticed that the False value (meaning that the input string does not equal to a value in the array), was not correct (there were input values that were exactly the same a value in the array).
Please help,
Imagine what happens when the match is found in the middle of the array. The next iteration will be a mismatch, and the return value gets set back to False. You need to stop the loop when a match is found.
I believe the VBA syntax for that is
Exit For
I am trying to get the index of the item in an array VBA, but I have issue getting it.
Const NoOfVol As Integer = 5
Dim vol(NoOfVol) As Integer
For Index = 1 To NoOfVol
vol(Index) = Cells(15 + Index, 8).Value
Next
The array looks like this: (-2500,-1250,0,1250,2500). I am calling the function this way.
Function Find(ByVal Value As Variant, arr As Variant) As Integer
Find = Application.Match(Value, arr, False)
End Function
posOfVol = Find(-1250, vol)
But the posOfVol = 3, when it should be 2. Not sure where my error is. Need some guidance on this.
Your array is actually effectively declared as:
Dim vol(0 to NoOfVol) As Integer
unless you have an Option Base 1 statement. Since your loop goes from 1 to 5, you never populate the first element of the array and it retains its default value of 0. Therefore your array is actually:
(0,-2500,-1250,0,1250,2500)
and since you are looking for an exact match, -1250 is found at the third position.