I was working on a small development issue today where the client wanted to be able to select a value from a multi-value list and then remove that entry and entries in the same position from a number of other multi-value lists.
The existing code for this function was written in Formula language and it just didn’t work properly so I set about rewriting it in LotusScript. A much neater solution…
First a small shared function…
Function removeElement(varFromList As Variant,varTarget As Integer) As Variant Redim arrReturn(0) As Variant Dim intCount As Integer intCount=0 For element = Lbound(varFromList) To Ubound(varFromList) If element <> varTarget Then Redim Preserve arrReturn(intCount) As Variant arrReturn(intCount)=varFromList(element) intCount=intCount+1 End If Next element removeElement=arrReturn End Function
Then to call the function it’s just a simple bit of code
Selection = w.Prompt(4,"Select Part To Remove","Please select the part number _
that you wish to remove from the list","",doc.part) If Selection = "" Then Exit Sub SelectionNo = Arraygetindex(doc.Part,Selection) doc.Part = removeElement(doc.Part,SelectionNo)
This LotusScript was converted to HTML using the ls2html routine,
provided by Julian Robichaux at nsftools.com.
provided by Julian Robichaux at nsftools.com.
Thanks for the ‘formula language’ version Robert. The one that was in the database I was using was so unreadable that I couldn’t make head nor tail of of it. I think the LotusScript version is nicer however when you have a load of fields to do.
I needed to get rid of a ‘Part’ listing across 6 fields. Part, Quantity, Unit Price etc. With the script version you just have to write the function once and then call it for each field you want to remove the element from.
LikeLike
Oh, for shame, Declan! Surely a developer of your caliber can see when he’s coding a base function with bad performance aspects. Sure, if your multi-value list has 5 elements, we probably won’t care. But make it 1000 or so and see what happens with your removeElement function!
Because I want our latest OpenNTF star to give good advice to his audience, let’s optimize. 🙂
First, you’ve got a Redim Preserve. Now in general, a Redim Preserve is to be avoided, but what’s really important is to avoid it INSIDE a loop. If you’ve got 5 elements, you’re reallocating and building the array in new memory 4 times with this function. But if you’ve got 1000, you’re reallocating and populating memory 999 times! Try doing that on a scalable web page!
In truth, the fact that your only removing a single element with this function means you can make it super-efficient. You know that you’re going to end up with an array that’s the original size minus one, so there’s all kinds of neat tricks we can do with that.
If we were willing to accept that we’re always going to modify the original array, we could even remove the element in place. Which would be a huge benefit if we want to remove element 999 of 1000.
Function removeElement(varFromList As Variant,varTarget As Integer) As Variant
Dim intCount As Integer
intCount = varTarget
For element = varTarget To Ubound(varFromList)-1
intCount=intCount+1
varFromList(element)=varFromList(intCount)
Next element
varFromList(ubound(varFromList)) = “” ‘we might want to check for numeric data, etc
fullTrim(varFromList)
removeElement=varFromList
End Function
But even if we don’t, we only have to set the size of the new array once. And we can make our loop more efficient by avoiding the (admittedly simple) comparison and counter.
Function removeElement(varFromList As Variant,varTarget As Integer) As Variant
Dim newArray() as Variant
Redim newArray(ubound(varFromList)-1)
For element = lbound(varFromList) to varTarget-1
newArray(element) = varFromList(element)
Next element
For element = varTarget+1 To Ubound(varFromList)
newArray(element-1) = varFromList(element)
Next element
removeElement=newArray
End Function
That’s it. There’s probably more to be done, but the point is, base functions like these should be coded with maximum efficiency, because they’re exactly the sort of thing that you put in a library and call 50 times on a single page. Then you deploy your Domino server, have 1000 people simultaneously connecting and you want to make sure that you’re not killing your performance with unnecessary memory allocation on a grand scale.
Hope this helps!
LikeLike
here the formula in R5:
1. case = use this if there could be same entries in list (like A:A:A:B:C)
– a field called DelElement/number => user enters to be removed element
– two multivalue fields, called “ListA” and “ListB”
– a button with that formula:
REM “which element to delete”;
var_element:=DelElement;
REM “Get multivalues from Field A and B”;
var_SourceA:=ListA;
var_SourceB:=ListB;
REM “some logical checks”;
@If(var_element>@Elements(var_SourceA);@Return(@Prompt([OK];”ayiieee”;”entered element number greater then number of entries!”));@Success);
@If(@Elements(var_SourceA)>@Elements(var_SourceB);@Return(@Prompt([OK];”ayiieee”;”number of entries in Field A and Field B is different!”));@Success);
REM “build new list, e.g. if 7th element to be removed then at first subset element 1-6 and than elements 8-10″;
var_NewListA:=
@trim(
@If(var_element < 2;””;@Subset( var_SourceA; var_element – 1 )) :
@If(var_element = @elements( var_SourceA );””; @Subset( var_SourceA; var_element – @elements( var_SourceA )))
) ;
var_NewListB:=
@trim(
@If(var_element < 2;””;@Subset( var_SourceB; var_element – 1 )) :
@If(var_element = @elements( var_SourceB );””; @Subset( var_SourceB; var_element – @elements( var_SourceB )))
) ;
Field ListA:=var_NewListA;
Field ListB:=var_NewListB;
void
2. case = use this if there are guaranteed no same entries in list (like A:B:C:D:E)
REM “which element to delete”;
var_element:=DelElement;
REM “Get multivalues from Field A and B”;
var_SourceA:=ListA;
var_SourceB:=ListB;
REM “some logical checks”;
@If(var_element>@Elements(var_SourceA);@Return(@Prompt([OK];”ayiieee”;”entered element number greater then number of entries!”));@Success);
@If(@Elements(var_SourceA)>@Elements(var_SourceB);@Return(@Prompt([OK];”ayiieee”;”number of entries in Field A and Field B is different!”));@Success);
REM “identify given element in Field A and B to be replaced”;
var_WhatA:=@Subset(@Subset(ListA;var_element);-1);
var_WhatB:=@Subset(@Subset(ListB;var_element);-1);
REM “replace element in Field A and B”;
var_newListA:=@Trim(@Replace(var_SourceA ; var_WhatA; “” ));
var_newListB:=@Trim(@Replace(var_SourceB ; var_WhatB; “” ));
FIELD ListA:=var_newListA;
FIELD ListB:=var_newListB;
_void
LikeLike
Robert, the fulltrim method would work ok provided that there are no other parts of the array with null values. If there is the possibility of null values in the array then FullTrim will get rid of them all.
Nathan, Yes, the method that you have described would be much better, especially for large arrays. I never said my function had been optimised, it just did what it needed in the clients database. When I’m back in there today I might change it to your version.
LikeLike
just a small question..ermm..im really not the big developer as you guys are, but does this code does not do the same and faster:
Dim WS As New NotesUIWorkspace
Dim UIDoc As NotesUIDocument
Set UIDoc = ws.CurrentDocument
Dim Doc As NotesDocument
Set doc = uidoc.document
dat = doc.MV
dat(0) = “”
datnew = Fulltrim(dat)
doc.MV = datnew
Call doc.save(True, False)
You could simply call the bold part into a function. No redims, no For loops, …am i overlooking something or is it so easy? I mean, Declan wants to remove a given element by an index.
LikeLike
arrResult=FullTrim(ArrayReplace(arrRemoveFrom, arrRemoveThis,{})):-)
LikeLike
im throwing Script away wherever i can due to perfomance aspects. In this case you are absolutely right with it: Script serves better than Formula. Just wanted to throw it in since there are always Domino guys/girls w/o Scripting know how.
btw…did you see the new R6 formulas? wow….i will try to translate your script into the new R6 environment.
cu
Rob
LikeLike
Sorry, didn’t read the latest posting…I admit if you need to preserve null values you’re into trouble…Maybe use ArrayAppend? But how to map e.g. arr(42..53) to arr(0..11) effectively w/o addressing each element? If there’s a way we can Redim Preserve array1 to 0..40, omit 41 and ArrayAppend arr(42..53).Sometimes lists are much easier to handle.. we could just use Erase(tag) and be done with it.Wolfgang
LikeLike