Ncalc how to evaluate multi-value string parameters - vb.net

I am using Ncalc to evaluate the presence of some string values
if (#Xval = 'Z','T','F')
this works well when #xval is inputted as a parameter as a single value(#Xval = 'Z'). That will return a true evaluation. I am now looking to evaluate the same formula when #Xval may be say 'Z','H' in other words Xval contains those 2 values and Im trying to find if 'Z' is among them.
The same goes for if (in (#Xval,'Z','H','M'),'T','F') where Im looking for the value of Xval in a group of options (Z,H,M).
Can I do this via custom functions? If so how? Any other ideas?
Thank you

You can try
Expression e = new Expression("if (iscontians("ZHM",#Xval),'T','F')", EvaluateOptions.IgnoreCase);
e.EvaluateFunction += evalFunction;
Write a custom function
private void evalFunction(string name, FunctionArgs args)
{
switch (name.ToUpper())
{
case "ISCONTAINS":
if (args.Parameters.Length < 2)
throw new ArgumentException("isContains() takes at least 2 arguments");
args.Result = args.Parameters[0].Evaluate().ToString().Contains(args.Parameters[1].Evaluate().ToString());
break;
default:
break;
}
}

Related

How to use Lambda expressions in java for nested if-else and for loop together

I have following Code where i will receive list of names as parameter.In the loop, first i'm assigning index 0 value from list to local variable name. There after comparing next values from list with name. If we receive any non-equal value from list, i'm assigning value of result as 1 and failing the test case.
Below is the Array list
List<String> names= new ArrayList<String>();
names.add("John");
names.add("Mark");
Below is my selenium test method
public void test(List<String> names)
String name=null;
int a=0;
for(String value:names){
if(name==null){
System.out.println("Value is null");
name=value;
}
else if(name.equals(value)){
System.out.println("Received Same name");
name=value;
}
else{
a=1;
Assert.fail("Received different name in between");
}
}
How can i convert above code into lambda expressions?. I'm using cucumber data model, hence i receive data as list from feature file. Since i can't give clear explanation, just posted the example logic i need to convert to lambda expression.
Here's the solution: it cycles all element in your list checking if are all the same.
You can try adding or editing the list so you can have different outputs. I've written the logic, you can easly put it into a JUnit test
List<String> names= new ArrayList<>();
names.add("John");
names.add("Mark");
String firstEntry = names.get(0);
boolean allMatch = names.stream().allMatch(name -> firstEntry.equals(name));
System.out.println("All names are the same: "+allMatch);
Are you looking for duplicates, whenever you have distinct value , set a=1 and say assert to fail. You can achieve this by :
List<String> names= new ArrayList<String>();
names.add("John");
names.add("Mark");
if (names.stream().distinct().limit(2).count() > 1) {
a= 1,
Assert.fail("Received different name in between");
} else {
System.out.println("Received Same name");
}

Checking for a specific value in an ArrayList

I need to determine if a element in my ArrayList has a specific value. This is the current way I am trying.
public static boolean hasDwgNamedReference(Object valueDWG){
boolean b = false;
String dwgName = "dwg_";
if(valueDWG == null){
b = false;
}
else {
String checkNamedRef = Arrays.toString((Object[]) valueDWG).substring(1, 5);
System.out.println("checkNamedRef " + checkNamedRef + "\n");
if(checkNamedRef.equals(dwgName)){
b = true;
}
}
return b;
}// end hasDwgNamedReference
I am pretty sure that the issue is with the
String checkNamedRef = Arrays.toString((Object[]) valueDWG).substring(1, 5);
Do you think I need to increment the ArrayList and check each element?
Well, firstly, you do not need to include the first "if" statement. b is already false, so setting it to false again is unnecessary.
If you use the Arrays.toString() method on an object, you may get the identity of the object, or the address in memory that it holds. If you try to compare to checkNamedRef via an equals method, you may be comparing an address and a string, which would never give you the true you are looking for.

Can I pass parameters to UDFs in Pig script?

I am relatively new to PigScript. I would like to know if there is a way of passing parameters to Java UDFs in Pig?
Here is the scenario:
I have a log file which have different columns (each representing a Primary Key in another table). My task is to get the count of distinct primary key values in the selected column.
I have written a Pig script which does the job of getting the distinct primary keys and counting them.
However, I am now supposed to write a new UDF for each column. Is there a better way to do this? Like if I can pass a row number as parameter to UDF, it avoids the need for me writing multiple UDFs.
The way to do it is by using DEFINE and the constructor of the UDF. So here is an example of a customer "splitter":
REGISTER com.sample.MyUDFs.jar;
DEFINE CommaSplitter com.sample.MySplitter(',');
B = FOREACH A GENERATE f1, CommaSplitter(f2);
Hopefully that conveys the idea.
To pass parameters you do the following in your pigscript:
UDF(document, '$param1', '$param2', '$param3')
edit: Not sure if those params need to be wrappedin ' ' or not
while in your UDF you do:
public class UDF extends EvalFunc<Boolean> {
public Boolean exec(Tuple input) throws IOException {
if (input == null || input.size() == 0)
return false;
FileSystem fs = FileSystem.get(UDFContext.getUDFContext().getJobConf());
String var1 = input.get(1).toString();
InputStream var1In = fs.open(new Path(var1));
String var2 = input.get(2).toString();
InputStream var2In = fs.open(new Path(var2));
String var3 = input.get(3).toString();
InputStream var3In = fs.open(new Path(var3));
return doyourthing(input.get(0).toString());
}
}
for example
Yes, you can pass any parameter in the Tuple parameter input of your UDF:
exec(Tuple input)
and access it using
input.get(index)

searching a list object

I have a list:
Dim list As New List(Of String)
with the following items:
290-7-11
1255-7-12
222-7-11
290-7-13
What's an easy and fast way to search if duplicate of "first block" plus "-" plus "second block" is already in the list. Example the item 290-7 appears twice, 290-7-11 and 290-7-13.
I am using .net 2.0
If you only want to know if there are duplicates but don't care what they are...
The easiest way (assuming exactly two dashes).
Boolean hasDuplicatePrefixes = list
.GroupBy(i => i.Substring(0, i.LastIndexOf('-')))
.Any(g => g.Count() > 1)
The fastest way (at least for large sets of strings).
HashSet<String> hashSet = new HashSet<String>();
Boolean hasDuplicatePrefixes = false;
foreach (String item in list)
{
String prefix = item.Substring(0, item.LastIndexOf('-'));
if (hashSet.Contains(prefix))
{
hasDuplicatePrefixes = true;
break;
}
else
{
hashSet.Add(prefix);
}
}
If there are cases with more than two dashes, use the following. This will still fail with a single dash.
String prefix = item.Substring(0, item.IndexOf('-', item.IndexOf('-') + 1));
In .NET 2.0 use Dictionary<TKey, TValue> instead of HashSet<T>.
Dictionary<String, Boolean> dictionary= new Dictionary<String, Boolean>();
Boolean hasDuplicatePrefixes = false;
foreach (String item in list)
{
String prefix = item.Substring(0, item.LastIndexOf('-'));
if (dictionary.ContainsKey(prefix))
{
hasDuplicatePrefixes = true;
break;
}
else
{
dictionary.Add(prefix, true);
}
}
If you don't care about readability and speed, use an array instead of a list, and you are a real fan of regular expressions, you can do the following, too.
Boolean hasDuplicatePrefixes = Regex.IsMatch(
String.Join("#", list), #".*(?:^|#)([0-9]+-[0-9]+-).*#\1");
Do you want to stop user from adding it?
If so, a HashTable with the key as first block-second block could be of use.
If not, LINQ is the way to go.
But, it will have to traverse the list to check.
How big can this list be?
EDIT: I don't know if HashTable has generic version.
You could also use SortedDictionary which can take generic arguments.
If you're list contains only strings, then you can simply make a method that takes the string you want to find along with the list:
Boolean isStringDuplicated(String find, List<String> list)
{
if (list == null)
throw new System.ArgumentNullException("Given list is null.");
int count = 0;
foreach (String s in list)
{
if (s.Contains(find))
count += 1;
if (count == 2)
return true;
}
return false;
}
If you're numbers have a special significance in your program, don't be afraid to use a class to represent them instead of sticking with strings. Then you would have a place to write all the custom functionality you want for said numbers.

Add 10000 to numbers using Regex replace?

I need to replace some 2- and 3-digit numbers with the same number plus 10000. So
Photo.123.aspx
needs to become
Photo.10123.aspx
and also
Photo.12.aspx
needs to become
Photo.10012.aspx
I know that in .NET I can delegate the replacement to a function and just add 10000 to the number, but I'd rather stick to garden-variety RegEx if I can. Any ideas?
James is right that you want to use the Regex.Replace method that takes a MatchEvaluator argument. The match evaluator delegate is where you can take the numeric string you get in the match and convert it into a number that you can add 10,000 to. I used a lambda expression in place of the explicit delegate because its more compact and readable.
using System;
using System.Text.RegularExpressions;
namespace RenameAspxFile
{
sealed class Program
{
private static readonly Regex _aspxFileNameRegex = new Regex(#"(\S+\.)(\d+)(\.aspx)", RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase);
private static readonly string[] _aspxFileNames= {"Photo.123.aspx", "Photo.456.aspx", "BigPhoto.789.aspx"};
static void Main(string[] args)
{
Program program = new Program();
program.Run();
}
void Run()
{
foreach (string aspxFileName in _aspxFileNames)
{
Console.WriteLine("Renamed '{0}' to '{1}'", aspxFileName, AddTenThousandToPhotoNumber(aspxFileName));
}
}
string AddTenThousandToPhotoNumber(string aspxFileName)
{
return _aspxFileNameRegex.Replace(aspxFileName, match => String.Format("{0}{1}{2}", match.Result("$1"), Int32.Parse(match.Result("$2")) + 10000, match.Result("$3")));
}
}
}
I think that using a RegEx for the match, and a function for the replace is most appropriate in this case, you are doing simple math, use something that is designed to do it.....
Is there any reason it has to be VB.NET?
Perl
s(
Photo\. (\d{2,3}) \.aspx
){
"Photo." . ($1 + 10000) . ".aspx"
}xe
Try the following:
"Photo\./d\.aspx" and replace with "Photo.1000$1.aspx"
"Photo\./d/d\.aspx" and replace with "Photo.100$1.aspx"
"Photo\./d/d/d\.aspx" and replace with "Photo.10$1.aspx"
That is the only way I see this happening.
If it's only two or three digit numbers:
(I assume you are using .NET Regex since we are talking about .aspx files)
Check for: Photo\.{\d\d\d}\.aspx
Replace with: Photo.10\1.aspx
Then check against: Photo\.{\d\d}\.aspx
Replace with: Photo.100\1.aspx
James Curran did it little faster than me but well here is what I have for you. Think it's the smallest code you can have with Regex to do what you want.
Regex regex = new Regex(#"(\d\d\d?)", RegexOptions.None);
string result = regex.Replace(#"Photo.123.asp", delegate(Match m)
{
return "Photo.1"
+ m.Groups[1].Captures[0].Value.PadLeft(4, '0')
+ ".aspx";
}
);
did you try just using PadLeft?
This appears to do what you want:
static public string Evaluator(Match match)
{
return "Photo.1"
+ match.Groups[1].Captures[0].Value.PadLeft(4, '0')
+ ".aspx";
}
public void Code(params string[] args)
{
string pattern = #"Photo\.([\d]+)\.aspx";
string test = "Photo.123.aspx";
Regex regex = new Regex(pattern);
string converted = regex.Replace(test, Evaluator)
Console.WriteLine(converted);
}
This will match the right part of the string, but won't tell you if it's two digits or three.
[^\d][\d]{2,3}[^\d]
Still, you could use that to grab the number, convert it to an int, add 10000, and convert that to the string you need.
Found this question since I was trying to do something similar in Vim.
Ill put the solution here.
:s/Photo\.\d\+\.aspx/\=Photo\.submatch(0)+10000\.aspx/g