There is a client notes application and in its main form, there is a dialogList having this formula: ( C21331:312321C is, actually, the replica ID, in this example it's just a random number )
_view := "vw1";
_vieww :="vw1";
x :=#Sort(#Unique ( #DbColumn( "" : "NoCache" ; C21331:312321C; _view; 1 ) );[Ascending]);
y :=#Unique ( #DbColumn( "" : "NoCache" ; #DbName ; _vieww ; 1));
y:x
How to use thee above code in my combobox computed values? I tried:
var _view = "vw1";
var _vieww= "vw2";
db = new Array(#Sort(#Unique ( #DbColumn( "" : "NoCache" , C21331:312321C, _view, 1 ) );[Ascending]);
db1 = new Array(#Unique ( #DbColumn( "" : "NoCache" ; #DbName ; _vieww ; 1)));
db:db1
How can I achieve this functionalty in xpages? Thanks for your time.
Use .sort() to sort an array (#Sort is not implemented in SSJS) and .concat() to concatenate two arrays or an array and a string:
var a = [].concat(#Unique(#DbColumn( ... ))).sort();
var b = #Unique(#DbColumn( ... ));
return a.concat(b);
Related
My Problem is to integrate an if-statement into a string template.
TYPES: BEGIN OF ty_field,
name TYPE string,
label TYPE string,
result TYPE string,
END OF ty_field.
DATA: mt_fields TYPE TABLE OF ty_field.
mt_fields = VALUE #(
( name = 'HI' label = 'Greeting' )
( name = 'PERSON' label = 'Who' )
( name = 'RESULT' label = 'Sentence' result = |Say \{HI\} to \{PERSON\}.| ) ).
Unfortunately the line 3 of mt_fields has result set to Say {HI} to {PERSON} but I'd expect Say Greeting to Who.
More importantly, what I want to get:
Some times there is no data for PERSON. In that case, the result variable will get the string 'Say hello to .' (if 'hello' is the value for HI).
I dont want to have this. If there is no person, I want to get an empty string as result. So I have to filter first, whether there is some value for PERSON or not. I need to get an if-condition into the string assignment.
In Python I would write something like
( name = 'RESULT' label = 'Sentence' result = 'Say {HI} to {PERSON}.' IF PERSON != '' ELSE '' )
But how to do this in ABAP?
Thanks a lot!
If I understood correctly what you are trying to do:
TYPES: BEGIN OF ty_field,
name TYPE string,
label TYPE string,
result TYPE string,
END OF ty_field.
DATA: mt_fields TYPE TABLE OF ty_field.
mt_fields = VALUE #( let salutation = 'Greeting' pers = cond string( when salutation = 'Hello' then '' else 'Who' ) IN
( name = 'HI' label = salutation )
( name = 'PERSON' label = pers )
( name = 'RESULT' label = 'Sentence' result = |Say { salutation } to { pers }.| ) ).
I want to insert some big values in an excel sheet, but it is giving errors.
Error
Text values in formulas are limited to 255 characters. To create text
values longer in a formula, use the CONCATENATE
function or the concatenation operator (&).
Value
=CONCATENATE("BEGIN TRY
BEGIN TRANSACTION
BEGIN --User defined values
DECLARE #FieldName NVARCHAR(100) = '",F2,"';
DECLARE #Currencycode VARCHAR(3) = '",A2,"';
DECLARE #Countrycode VARCHAR(2) = '",B2,"';
DECLARE #RuleType NVARCHAR(100) = 'CannotContainSpecialCharOtherThan';
DECLARE #RuleValue INT = 0;
DECLARE #InsFieldTypeDesc NVARCHAR(50) = 'AnyType';
DECLARE #RuleValueAlpha NVARCHAR(4000) = '/: (),.''-?+';
DECLARE #ErrMsg NVARCHAR(500) = '",N2,"';
DECLARE #ErrCode NVARCHAR(10) = '",M2,"';
DECLARE #ErrPrior TINYINT = '",L2,"';
DECLARE #IsLength INT = 0; ---if length is there then set this 1 or 0
DECLARE #DependantOn NVARCHAR(100) = NULL
END
BEGIN --Consts
DECLARE #UTCTime DATETIME = Getutcdate();
DECLARE #CTTime DATETIME = Dateadd(hour, -5, Getutcdate());
END
DECLARE #FieldRuleConfigPk INT;
DECLARE #ErrorMsgpk INT;
DECLARE #countryfk INT;
DECLARE #Fieldnamefk INT;
DECLARE #InsFieldTypeFk INT;
DECLARE #RuleValue1 INT = 0;
DECLARE #RuleValue2 INT = 19;
DECLARE #RuleValue3 INT = 19;
DECLARE #RuleTypeFk INT;
DECLARE #ErrorFk INT;
DECLARE #IsOk INT = 0
PRINT( 'Starts script' )
BEGIN --INIT
SET #countryfk = (SELECT countrypk
FROM mas_country
WHERE countrycode = #Countrycode);
SET #Fieldnamefk = (SELECT fieldnamepk
FROM mas_fieldname
WHERE fieldname = #FieldName);
SET #InsFieldTypeFk = (SELECT instructionfieldtypepk
FROM mas_instructionfieldtype
WHERE fieldtypedesc = #InsFieldTypeDesc);
END
IF NOT EXISTS (SELECT 1
FROM mas_fieldruleconfig
WHERE currencycode = #Currencycode
AND countryfk = #countryfk
AND fieldnamefk = #Fieldnamefk)
BEGIN
INSERT INTO mas_fieldruleconfig
(currencycode,
countryfk,
fieldnamefk,
instructionfieldtypefk,
createddateutc,
createddatect)
VALUES ( #Currencycode,
#countryfk,
#Fieldnamefk,
#InsFieldTypeFk,
#UTCTime,
#CTTime )
SELECT #FieldRuleConfigPk = Scope_identity();
SET #IsOk = 1
PRINT( 'mas_fieldruleconfig insert success' )
END
ELSE
BEGIN
SELECT #FieldRuleConfigPk = fieldruleconfigpk
FROM mas_fieldruleconfig
WHERE currencycode = #Currencycode
AND countryfk = #countryfk
AND fieldnamefk = #Fieldnamefk
PRINT( 'mas_fieldruleconfig setting already exists' )
END
IF NOT EXISTS (SELECT 1
FROM mas_ruletype
WHERE ruletype = #RuleType
AND fieldruleconfigfk = #FieldRuleConfigPk
--Need to add this check, otherwise it will fail
AND rulevalue = #RuleValue --Not needed
AND rulevaluealphanumeric = #RuleValueAlpha)
--Not needed
BEGIN
INSERT INTO mas_ruletype
(ruletype,
fieldruleconfigfk,
rulevalue,
rulevaluealphanumeric,
createddateutc,
createddatect,
dependanton)
VALUES ( #RuleType,
#FieldRuleConfigPk,
#RuleValue,
#RuleValueAlpha,
#UTCTime,
#CTTime,
#DependantOn)
SELECT #RuleTypeFk = Scope_identity();
SET #IsOk = 1
PRINT( 'mas_ruletype insert success' )
END
ELSE
BEGIN
SELECT #RuleTypeFk = (SELECT ruletypepk
FROM mas_ruletype
WHERE ruletype = #RuleType
AND fieldruleconfigfk =
#FieldRuleConfigPk
--Need to add this check, otherwise it will fail
AND rulevalue = #RuleValue
--Not needed
AND rulevaluealphanumeric =
#RuleValueAlpha)
PRINT( 'mas_ruletype settings already exists' )
END
IF NOT EXISTS (SELECT 1
FROM mas_errormessage
WHERE errormessage = #ErrMsg)
BEGIN
INSERT INTO mas_errormessage
(errormessage,
createddateutc,
createddatect)
VALUES ( #ErrMsg,
#UTCTime,
#CTTime )
SELECT #ErrorMsgpk = Scope_identity()
SET #IsOk = 1
PRINT( 'mas_errmsg insert success' )
END
ELSE
BEGIN
SELECT #ErrorMsgpk = errormessagepk
FROM mas_errormessage
WHERE errormessage = #ErrMsg
PRINT( 'mas_errormsg settings already exists' )
END
IF NOT EXISTS (SELECT 1
FROM mas_error
WHERE fieldnamefk = #Fieldnamefk
AND errorcode = #ErrCode)
BEGIN
INSERT INTO mas_error
(fieldnamefk,
errorcode,
errorpriority,
errormessagefk,
createddateutc,
createddatect)
VALUES ( #Fieldnamefk,
#ErrCode,
#ErrPrior,
#ErrorMsgpk,
#UTCTime,
#CTTime )
SELECT #ErrorFk = Scope_identity();
SET #IsOk = 1
PRINT( 'mas_error insert success' )
END
ELSE
BEGIN
SELECT #ErrorFk = (SELECT errorpk
FROM mas_error
WHERE fieldnamefk = #Fieldnamefk
AND errorcode = #ErrCode);
PRINT( 'Mas_Error settings already exists' )
END
IF NOT EXISTS (SELECT 1
FROM [lnk_fieldruleerror]
WHERE [fieldruleconfigfk] = #FieldRuleConfigPk
AND [fieldnamefk] = #Fieldnamefk
AND [ruletypefk] = #RuleTypeFk
AND #ErrorFk = #ErrorFk)
BEGIN
INSERT INTO [dbo].[lnk_fieldruleerror]
([fieldruleconfigfk],
[fieldnamefk],
[ruletypefk],
[errorfk])
VALUES (#FieldRuleConfigPk,
#Fieldnamefk,
#RuleTypeFk,
#ErrorFk)
END
IF #IsOk = 1
BEGIN
COMMIT TRANSACTION
PRINT( 'commit' )
END
END TRY
BEGIN CATCH
PRINT 'Error'
PRINT ( 'Rollback' )
ROLLBACK TRANSACTION;
END CATCH")
I have followed this link also but could not find a solution.
Try breaking it up like this:
=CONCATENATE("BEGIN TRY",CHAR(10),
"BEGIN TRANSACTION",CHAR(10),
"dgd",CHAR(10),
"dfh",CHAR(10),
etc.
);
I have solved by splitting the value into multiple cells and merging these into a new cell like this =A2&" "&B2
I modified the code to work with two files. to_search.txt has string to be searched. big_file.fastq has lines where to be searched and if string found (2 mismatch allowed with exact length which range from 8-10, no addition and deletion), place in respective name. So each string is searched in all lines (2nd line) in big_file.fastq.
# to_search.txt: (length can be from 8-20 characters)
1 TCCCTTGT
2 ACGAGACT
3 GCTGTACG
4 ATCACCAG
5 TGGTCAAC
6 ATCGCACA
7 GTCGTGTA
8 AGCGGAGG
9 ATCCTTTG
10 TACAGCGC
#2000 search needed
# big_file.fastq: 2 billions lines (each 4 lines are associated: string search is in second line of each 4 lines).
# Second line can have 100-200 characters
#M04398:19:000000000-APDK3:1:1101:21860:1000 1:N:0:1
TCttTTGTGATCGATCGATCGATCGATCGGTCGTGTAGCCTCCAACCAAGCACCCCATCTGTTCCAAATCTTCTCCCACTGCTACTTGAAGACGCTGAAGTTGAAGGGCCACCTTCATCATTCTGG
+
#8ACCDGGGGGGGGGGGGGEGGGGGGGGDFFEGGG#FFGGGGGGGGGGGGGGGGGCF#<FFGGGGGFE9FGGGFEACE8EFFFGGGGGF9F?CECEFEG,CFGF,CF#CCC?BFFG<,9<9AEFG,,
#M04398:19:000000000-APDK3:1:1101:13382:1000 1:N:0:1
NTCGATCGATCGATCGATCGATCGATCGTTCTGAGAGGTACCAACCAAGCACACCACGGGCGACACAGACAGCTCCGTGTTGAACGGGTTGTTCTTCTTCTTGCCTTCATCATCCCCATCCTCAGTGGACGCAGCTTGCTCATCCTTCCTC
+
#8BCCGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
#M04398:19:000000000-APDK3:1:1101:18888:1000 1:N:0:1
NCAGAATGAGGAAGGATGAGCCCCGTCGTGTCGAAGCTATTGACACAGCGCTATTCCGTCTTTATGTTCACTTTAAGCGGTACAAGGAGCTGCTTGTTCTGATTCAGGAACCGAACCCTGGTGGTGTGCTTGGTTGGCAAGTTTACGGCTC
+
#8BCCGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGCGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGFGGGGGGGGGGGGGGGGGGGGGGGGGGGE
Here is the code for two mismatches. I tried with exact match, speed is not bad: takes around a day. I have used Time::Progress module. When I use 2 mismatch: shows 115 days to finish. How the speed can be improved here?
#!/usr/bin/perl
use strict;
use warnings;
$| = 1;
open( IN_P1, "big_file.fastq" ) or die "File not found";
my ( #sample_file_names, #barcode1 );
open( BC_FILE, "to_search.txt" ) or die "No barcode file";
my #barcode_file_content = <BC_FILE>;
foreach (#barcode_file_content) {
chomp $_;
$_ =~ s/\r//;
$_ =~ s/\n//;
#print $_;
my #elements = split( "(\t|,| )", $_ );
push #sample_file_names, $elements[0];
push #barcode1, $elements[2];
}
# open FH
my #fh_array_R1;
foreach (#sample_file_names) {
chomp $_;
local *OUT_R1;
open( OUT_R1, ">", "$_\.fq" ) or die "cannot write file";
push #fh_array_R1, *OUT_R1;
}
# unknown barcode file
open( UNKNOWN_R1, ">unknown-barcode_SE.fq" ) or die "cannot create unknown-r1 file";
while ( defined( my $firstp1 = <IN_P1> ) ) {
my $p1_first_line = $firstp1;
my $p1_second_line = <IN_P1>;
my $p1_third_line = <IN_P1>;
my $p1_fourth_line = <IN_P1>;
chomp( $p1_first_line, $p1_second_line, $p1_third_line, $p1_fourth_line, );
my $matched_R1 = "$p1_first_line\n$p1_second_line\n$p1_third_line\n$p1_fourth_line\n";
for ( my $j = 0 ; $j < scalar #barcode1 ; $j++ ) {
chomp $barcode1[$j];
my $barcode1_regex = make_barcode_fragments( $barcode1[$j] );
if ( $p1_second_line =~ /$barcode1_regex/i ) {
# keep if matched
print { $fh_array_R1[$j] } $matched_R1;
last;
}
else {
#print to unknown;
print UNKNOWN_R1 $matched_R1;
}
}
}
# make two mismatch patterm of barcode
sub make_barcode_fragments {
my ($in1) = #_;
my #subpats;
for my $i ( 0 .. length($in1) - 1 ) {
for my $j ( $i + 1 .. length($in1) - 1 ) {
my $subpat = join( '',
substr( $in1, 0, $i ),
'\\w', substr( $in1, $i + 1, $j - $i - 1 ),
'\\w', substr( $in1, $j + 1 ),
);
push #subpats, $subpat;
}
}
my $pat = join( '|', #subpats );
#print $pat;
return "$pat";
}
exit;
If your algorithm cannot be changed/improved in Perl itself, you can still get speedup by writing the time consuming parts in C. Here is an example using inline C:
use strict;
use warnings;
use Benchmark qw(timethese);
use Inline C => './check_line_c.c';
my $find = "MATCH1";
my $search = "saasdadadadadasd";
my %sub_info = (
c => sub { check_line_c( $find, $search ) },
perl => sub { check_line_perl( $find, $search ) },
);
timethese( 4_000_000, \%sub_info );
sub check_line_perl {
my ($find, $search ) = #_;
my $max_distance = 2;
for my $offset ( 0 .. length($search) - length($find) ) {
my $substr = substr( $search, $offset, length($find) );
my $hd = hd( $find, $substr );
if ( $hd <= $max_distance ) {
return ( $hd, $substr );
}
}
return ( undef, undef );
}
sub hd {
return ( $_[0] ^ $_[1] ) =~ tr/\001-\377//;
}
where check_line_c.c is:
void check_line_c( char* find, char * search ) {
int max_distance = 2;
int flen = strlen(find);
int last_ind = strlen(search) - flen;
SV *dis = &PL_sv_undef;
SV *match = &PL_sv_undef;
for ( int ind = 0; ind <= last_ind; ind++ )
{
int count = 0;
for ( int j = 0; j < flen; j++ )
{
if ( find[j] ^ search[ind+j] ) count++;
}
if ( count < max_distance )
{
match = newSV(flen);
sv_catpvn(match, search+ind, flen );
dis = newSViv(count);
break;
}
}
Inline_Stack_Vars;
Inline_Stack_Reset;
Inline_Stack_Push(sv_2mortal(dis));
Inline_Stack_Push(sv_2mortal(match));
Inline_Stack_Done;
}
The output is (Ubuntu Laptop using Intel Core i7-4702MQ CPU #2.20GHz):
Benchmark: timing 4000000 iterations of c, perl...
c: 2 wallclock secs ( 0.76 usr + 0.00 sys = 0.76 CPU) # 5263157.89/s (n=4000000)
perl: 19 wallclock secs (18.30 usr + 0.00 sys = 18.30 CPU) # 218579.23/s (n=4000000)
So this gives a 24-fold speedup for this case.
I'd suggest creating a really bad hashing algorithm. Something nice and reversible and inefficient, like the sum of the characters. Or maybe the sum of unique values (1-4) represented by the characters.
Compute the target sums, and also compute the maximum allowed variance. That is, if the objective is a match with two substitutions, then what is the maximum possible difference? (4-1 + 4-1 = 6).
Then, for each "window" of text of the appropriate length in the target data file, compute a running score. (Add a character to the end, drop a character from the start, update the hash score.) If the score for a window is within the allowable range, you can do further investigation.
You might want to implement this as different passes. Possibly even as different stages in a shell pipeline or script. The idea being that you might be able to parallelize parts of the search. (For instance, all the match strings with the same length could be searched by one process, since the hash windows are the same.)
Also, of course, it is beneficial that you can keep your early work if your program crashes in the later stages. And you can even have the early parts of the process running while you are still developing the end stages.
I am hoping someone can put me out of my misery here. I rewrote a LotusScript function in JavaScript and it is not working as it should be. I can't look at this thing anymore. I"m hoping someone can help me figure this out.
The function takes a name of a group and an address book db object. It fetches and returns all the members of that group including nested groups. It works great in the Lotusscript version but not in the Javascript version. It's something to do with the recursive function and the index into the array's.
If you create two groups. Put 10 names in the first group and 5 names in the second group. Note, Name the second group with a leading # to make it sort to the top of the first group
group-A
1group-A
bla
bla2
bla3
bla4
bla5
bla6
bla7
bla8
bla9
bla10
1group-A
other1
other2
other3
other4
other5
In the Javascript version when you pass the function group-A it returns 10 names. Five from the second group and the second five from the first group.
Here is the code for the lotusscript version
Function LibGetGroupMembers( groupName As String, addressBook As NotesDatabase ) As Variant
' This Function takes a passed in group name and gets all the members of that group.
' Not a big whoop until you consider nested group names. This is a recursive routine.
'
' Inputs : Name of the group to fetch the members of.
'
' Uses : Global objects
'
' Outputs : Returns an array of members
'
' History:
' 02.19.2001 - Steven Rieger : Created Function.
Dim groupView As NotesView
Dim groupMainDoc As NotesDocument
Dim groupMembers As Variant
Dim nestedGroupMembers As Variant
Dim arrayOfMembers() As String
Dim mainIndex As Integer
Dim nestedIndex As Integer
Dim resultIndex As Integer
On Error Goto ErrorHandling
gLibObjectName = CStr( GetThreadInfo(1) ) & " - " & gLibDatabaseNamePath
gLibErrorMessage = "Begin " & gLibObjectName
Call LibLogErrorPushCallingPgm ( gLibErrorMessage )
Print( gLibErrorMessage )
gLibAdditionalErrorMessage = "Fetching Group View From NAB"
Set groupView = addressBook.GetView( "($VIMGroups)" )
If( groupView Is Nothing ) Then
Call LibLogError( "Error: Un-able to get view from address book", gLibErrorMessage )
Exit Function
End If
gLibAdditionalErrorMessage = "Fetching Group Main Document " & groupName
Set groupMainDoc = groupView.GetDocumentByKey( groupName, True )
resultIndex = 0
Redim arrayOfMembers( 0 )
' Did we find a matching group document?
If Not( groupMainDoc Is Nothing ) Then
If Not( Iselement ( gListOfGroupNames( Lcase( groupName ) ) ) ) Then
' Print( "Processing Group: " & groupName )
gListOfGroupNames( Lcase( groupName ) ) = " "
groupMembers=groupMainDoc.GetItemValue( "Members" )
For mainIndex = Lbound( groupMembers ) To Ubound( groupMembers)
nestedGroupMembers= LibGetGroupMembers( groupMembers( mainIndex ), addressBook )
If( nestedGroupMembers(0) = "" ) Then
If( groupMembers( mainIndex ) <> "" ) Then
Redim Preserve arrayOfMembers( Ubound( arrayOfMembers ) + 1)
arrayOfMembers( resultIndex ) = groupMembers( mainIndex )
resultIndex = resultIndex + 1
End If
Else
Redim Preserve arrayOfMembers( Ubound( nestedGroupMembers ) + resultIndex )
For nestedIndex = Lbound( nestedGroupMembers ) To Ubound( nestedGroupMembers )
arrayOfMembers( resultIndex ) = nestedGroupMembers( nestedIndex )
resultIndex = resultIndex + 1
Next
End If
Next
If( arrayOfMembers( Ubound( arrayOfMembers ) ) = "" And Ubound( arrayOfMembers ) > 0 ) Then
Redim Preserve arrayOfMembers( Ubound( arrayOfMembers ) - 1 )
End If
Else
groupName = "*** Circular Group: " & groupName & " ***"
End If
End If
LibGetGroupMembers = arrayOfMembers
Goto Bye
ErrorHandling :
Call LibLogError( "Error: " & Err( ) & ": " & Error( ) & Chr( 13 ) & Chr( 13 ) & "Occured in Module " & CStr( GetThreadInfo(1) ) & " at Line Number: " & Erl(), CStr( GetThreadInfo(1) & " - " & gLibDatabaseNamePath ) )
Resume Bye
Bye:
gLibAdditionalErrorMessage = "End " & CStr( GetThreadInfo(1) & " - " & gLibDatabaseNamePath )
' This will pop the last entry of the stack!
Call libLogErrorPopCallingPgm()
Print( gLibAdditionalErrorMessage )
End Function
HERE IS THE CODE FOR THE JAVASCRIPT FUNCTION
function jsLibGetGroupMembers( groupName:string, addressBook:NotesDatabase )
{
// This Function takes a passed in group name and gets all the members of that group.
// Not a big whoop until you consider nested group names. This is a recursive routine.
//
// Inputs : Name of the group to fetch the members of.
//
// Uses : Global objects
//
// Outputs : Returns an array of members
//
// History:
// 02.19.2001 - Steven Rieger : Created Function.
var groupView:NotesView;
var groupMainDoc:NotesDocument;
var groupMembers:java.util.Vector;
var nestedGroupMembers = new Array();
var arrayOfMembers = new Array();
var mainIndex:Integer;
var nestedIndex:Integer;
var resultIndex:Integer;
print( "Begin jsLibGetGroupMembers - Passed in Name: " + groupName );
groupView = addressBook.getView( "($VIMGroups)" )
if( groupView == null )
{
print( "group document not found!" );
return nestedGroupMembers;
}
// gLibAdditionalErrorMessage = "Fetching Group Main Document " & groupName
groupMainDoc = groupView.getDocumentByKey( groupName, true )
resultIndex = 0
// Did we find a matching group document?
if( groupMainDoc != null )
{ // is the group name already in the global list of groups?
if( !(groupName.toLowerCase() in gListOfGroupNames ) )
{
// Add the group name to the globla list of groups to prevent processing duplicate groups
gListOfGroupNames[ groupName.toLowerCase() ] = " "
groupMembers = groupMainDoc.getItemValue( "Members" );
for( groupMemberIndex = 0; groupMemberIndex < groupMembers.length; groupMemberIndex++ )
{
// Fetch any nested group members if the current member is a group name
nestedGroupMembers = jsLibGetGroupMembers( groupMembers[groupMemberIndex], addressBook );
if ( typeof nestedGroupMembers[0] === 'undefined' || nestedGroupMembers[0] === null )
{
// If no group was found and we have an actual member name add it to the list.
if( groupMembers[groupMemberIndex].length > 0 )
{
print( "adding user to array: " + groupMembers[groupMemberIndex] + " resultIndex = " + resultIndex );
arrayOfMembers[resultIndex] = groupMembers[groupMemberIndex];
resultIndex += 1;
}
else
{
print( "No User to Add!");
}
}
else
{
// If this was a nested group we found add the members of that group to the list
for( nestedGroupMemberIndex = 0; nestedGroupMemberIndex < nestedGroupMembers.length; nestedGroupMemberIndex++ )
{
print( "adding nested user to array: " + nestedGroupMembers[nestedGroupMemberIndex] + " resultIndex = " + resultIndex );
arrayOfMembers[ resultIndex ] = nestedGroupMembers[nestedGroupMemberIndex];
resultIndex += 1;
}
}
}
}
else
print( "Duplicate Group!");
}
else
{
print( "no group doc found!" );
}
print( "exiting jsLibGetGroupMembers: " + "\n" + arrayOfMembers );
return arrayOfMembers;
}
This answer will no doubt seem ironic given my recent blog post, but here's an SSJS implementation that will return a sorted list of all members of a group hierarchy:
var AtFunctions = (function() {
function getGroupNames (groupRecord, serverName) {
var result = new java.util.TreeSet();
if (groupRecord) {
for (var eachMember in groupRecord.getItemValue("Members")) {
var nestedGroupRecord = getGroupRecord(eachMember, serverName);
if (nestedGroupRecord) {
result.addAll(getGroupNames(nestedGroupRecord, serverName));
nestedGroupRecord.recycle();
} else {
result.add(eachMember);
}
}
}
return result;
}
function getGroupRecord (groupName, serverName) {
var result = null;
try {
serverName = (serverName || session.getServerName());
var NAB = session.getDatabase(serverName, "names.nsf");
var groupView = NAB.getView("($VIMGroups)");
result = groupView.getDocumentByKey(groupName, true);
} catch (e) {
print("Error getting group record: " + e.toString());
}
return result;
}
var API = {
expandNameList: function(groupName, serverName) {
var result = new java.util.TreeSet();
if (groupName) {
var groupRecord = getGroupRecord(groupName, serverName);
if (groupRecord) {
result.addAll(getGroupNames(groupRecord, serverName));
groupRecord.recycle();
}
}
return result;
}
};
return API;
})();
var #ExpandNameList = AtFunctions.expandNameList;
The variable alias at the end will allow you to treat this as a custom #Function, if desired, so you can call this one of two ways:
var groupMembers = #ExpandNameList("All Employees");
var groupMembers = AtFunctions.expandNameList("All Employees");
// in either syntax, you can optionally pass a server name as a second argument
NOTE: it's using the Java TreeSet class instead of JavaScript arrays, which has the following benefits:
It's automatically sorted. You might not want this, but for this
type of operation, it's often useful.
It automatically ignores duplicates, so you should only get one instance of each name even if the same member exists in multiple subgroups.
Syntactically, it's just so much easier to manipulate than an array.
Enjoy. :)
My assignment was to use the reference-based implementation of the ADT List and the array-based implementation of the ADT Stack in a program that has a user enter a string of lower-case letters. I was to go through the string and store each letter in both the list and the stack and then use the stack and list contents to determine if the string is a palindrome or not. I am to display the original sequence of letters, the sequence of letters in reverse order, and finally, a statement whether or not it is a palindrome or not. For some reason, when I input a palindrome, ex. madamimadam, it outputs that it is not a palindrome. I cannot figure out why, please help! Here is my code for the method:
import javax.swing.JOptionPane;
public class PalindromeTester
{
public static void main (String [] args)
{
Character ch;
boolean isPalindrome = true;
LinkedList myList = new LinkedList();
StackArrayBased myStack = new StackArrayBased();
String response = JOptionPane.showInputDialog ("Please enter a string of lower-case letters" ) ;
for ( int i = 0 ; i < response.length ( ) ; i++ )
{
ch = new Character ( response.charAt ( i ) ) ;
myStack.push ( ch ) ;
myList.add ( i + 1 , ch ) ;
}
System.out.println ( "The original sequence of characters is: " + response ) ;
System.out.print ( "The sequence of letters backwards is: " ) ;
int j = 1 ;
while ( ! myStack.isEmpty ( ) )
{
System.out.print ( myStack.peek ( ) ) ;
if ( ! myList.get ( j ).equals( myStack.pop ( ) ) ) ;
isPalindrome = false ;
}
if ( isPalindrome )
System.out.println ( "\nThe string is a palindrome." ) ;
else
System.out.println ( "\nThe string is not a palindrome." ) ;
}
}
Here is the ADT Stack class:
public class StackArrayBased
{
private static final int MAX_STACK = 15 ;
private Object items [ ] ;
private int top ;
public StackArrayBased ( )
{
items = new Object [ MAX_STACK ] ;
top = -1 ;
}
public boolean isEmpty ( )
{
return top < 0 ;
}
public boolean isFull ( )
{
return top == MAX_STACK - 1 ;
}
public void push ( Object newItem ) throws StackException
{
if ( ! isFull ( ) )
items [ ++ top ] = newItem ;
else
throw new StackException ( "StackException on push: stack is full" ) ;
}
public void popAll ( )
{
items = new Object [ MAX_STACK ] ;
top = -1 ;
}
public Object pop ( ) throws StackException
{
if ( ! isEmpty ( ) )
return items [ top -- ] ;
else
throw new StackException ( "StackException on pop: stack is empty" ) ;
}
public Object peek ( ) throws StackException
{
if ( ! isEmpty ( ) )
return items [ top ] ;
else
throw new StackException ( "StackException on peek: stack is empty" ) ;
}
}
and here is the ADT list:
public class LinkedList
{
private Node head;
private int numItems;
public LinkedList ( )
{
head = null ;
numItems = 0 ;
}
public boolean isEmpty ( )
{
return numItems == 0 ;
}
public int size ( )
{
return numItems ;
}
private Node find ( int position )
{
Node curr = head ;
for ( int i = 1 ; i < position ; i ++ )
curr = curr.getNext ( ) ;
return curr ;
}
public Object get ( int position )
{
if ( position >= 0 && position <= numItems )
{
Node curr = find ( position ) ;
Object dataItem = curr.getItem ( ) ;
return dataItem ;
}
else
{
System.out.println ( "Error in position value during get attempt." ) ;
return null ;
}
}
public void add ( int position, Object item )
{
if ( position >= 1 && position <= numItems + 1 )
{
if ( position == 1 )
{
Node newNode = new Node ( item, head ) ;
head = newNode ;
}
else
{
Node prev = find ( position - 1 ) ;
Node newNode = new Node ( item, prev.getNext ( ) ) ;
prev.setNext ( newNode ) ;
}
numItems ++ ;
}
else
System.out.println ( "Position is invalid on attempted add." ) ;
}
public void remove ( int position )
{
if ( position >= 1 && position <= numItems )
{
if ( position == 1 )
head = head.getNext ( ) ;
else
{
Node prev = find ( position - 1 ) ;
Node curr = prev.getNext ( ) ;
prev.setNext ( curr.getNext ( ) ) ;
}
numItems -- ;
}
else
System.out.println ( "Position is invalid on attempted remove." ) ;
}
public void removeAll ( )
{
head = null ;
numItems = 0 ;
}
}
If you want to set isPalindrome correctly, shouldn't you be doing something with j in this loop...?:
[...]
int j = 1 ;
while ( ! myStack.isEmpty ( ) )
{
System.out.print ( myStack.peek ( ) ) ;
if ( ! myList.get ( j ).equals( myStack.pop ( ) ) ) ;
isPalindrome = false ;
}
[...]
In the second loop, you should be incrementing j. Since linkedlist index can be 0, you shouldn't be doing i+1 index while adding ( in the first loop). If you make it a 0 based index you should be initalizing j to be 0 before the second loop.
The assignment seems odd; if you can access the last element of the list (as an abstract list allows in most language), then you can just do the for i=[0,length) {if input[i]!=input[length-1-i], return false} return true
And if you just had stacks to play with, you could just clone and reverse the stack (e.g. by pushing onto two stacks, and popping one of those into a new stack, thereby reversing it), and do the same thing as the for loop (go through the two stacks element-by-element to see if they're the same).
In both of the above linear-time algorithms (either the one that uses just lists, or the one that uses just stacks), the function should just be 3 lines or so.