Returning table with CLR - c#

I want to write an CLR procedure which takes a text and returns a table with all the words in this text. But I can't figure out how to return a table. Could you please tell me it?
[Microsoft.SqlServer.Server.SqlFunction]
public static WhatTypeShouldIWriteHere Function1(SqlString str)
{
string[] words = Regex.Split(str, #"\W+").Distinct().ToArray();
//how to return a table with one column of words?
}
Thank you for your help.
UPDATED: I need to do it for sql-2005

Here is a full blown sample. I got tired of searching for this myself and even though this is answered, I thought I would post this just to keep a fresh reference online.
using System;
using System.Data.SqlTypes;
using Microsoft.SqlServer.Server;
using System.Text.RegularExpressions;
using System.Collections;
using System.Collections.Generic;
public partial class UserDefinedFunctions {
[SqlFunction]
public static SqlBoolean RegexPatternMatch(string Input, string Pattern) {
return Regex.Match(Input, Pattern).Success ? new SqlBoolean(true) : new SqlBoolean(false);
}
[SqlFunction]
public static SqlString RegexGroupValue(string Input, string Pattern, int GroupNumber) {
Match m = Regex.Match(Input, Pattern);
SqlString value = m.Success ? m.Groups[GroupNumber].Value : null;
return value;
}
[SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "FillMatches", TableDefinition = "GroupNumber int, MatchText nvarchar(4000)")]
public static IEnumerable RegexGroupValues(string Input, string Pattern) {
List<RegexMatch> GroupCollection = new List<RegexMatch>();
Match m = Regex.Match(Input, Pattern);
if (m.Success) {
for (int i = 0; i < m.Groups.Count; i++) {
GroupCollection.Add(new RegexMatch(i, m.Groups[i].Value));
}
}
return GroupCollection;
}
public static void FillMatches(object Group, out SqlInt32 GroupNumber, out SqlString MatchText) {
RegexMatch rm = (RegexMatch)Group;
GroupNumber = rm.GroupNumber;
MatchText = rm.MatchText;
}
private class RegexMatch {
public SqlInt32 GroupNumber { get; set; }
public SqlString MatchText { get; set; }
public RegexMatch(SqlInt32 group, SqlString match) {
this.GroupNumber = group;
this.MatchText = match;
}
}
};

You can return any list that implements an IEnumerable. Check this out.

[SqlFunction(DataAccess = DataAccessKind.Read, FillRowMethodName = "FillMatches", TableDefinition = "GroupNumber int, MatchText nvarchar(4000)")]
public static IEnumerable Findall(string Pattern, string Input)
{
List<RegexMatch> GroupCollection = new List<RegexMatch>();
Regex regex = new Regex(Pattern);
if (regex.Match(Input).Success)
{
int i = 0;
foreach (Match match in regex.Matches(Input))
{
GroupCollection.Add(new RegexMatch(i, match.Groups[0].Value));
i++;
}
}
return GroupCollection;
}
That was a slight alteration from the code by "Damon Drake"
This one does a findall instead of returning the first value found.
so
declare #txt varchar(100) = 'Race Stat 2017-2018 -(FINAL)';
select * from dbo.findall('(\d+)', #txt)
returns

This is a new area of SQL Server, you should consult this article. Which shows the syntax of a table-valued function -- that is what you want to create.

Related

Search multiple column values with one search string

I have this query where I want to return results if the search string provided yields data from the 'FirstName' and 'LastName' column of my database. They each work individually as I can get results for Firstname = 'john' and LastName = 'doe'. I also want to be able to pass in a search string of 'John doe' and get results. How would I implement this using .net/linq
snippet:
var query = _context.Players.Where(p => p.Firstname.Contains(searchString.ToLower().Trim()) || p.Lastname.Contains(searchString.ToLower().Trim()));
use Split function like the following:
var parts = searchString.Split();
snippet: var query = _context.Players.Where(
p => p.Firstname.Contains(parts[0].ToLower().Trim())
|| p.Lastname.Contains(parts[1].ToLower().Trim()));
Extracted from the official docs:
If the separator parameter is null or contains no characters, white-space characters are assumed to be the delimiters.
Separating the input data is also convenient
var parts = searchString.Split();
var partOne = parts[0].ToLower().Trim();
var partTwo = parts[1].ToLower().Trim()
var query = _context.Players.Where(
p => p.Firstname.Contains(partOne)
|| p.Lastname.Contains(partTwo));
Created an extension method class to isolate the functional parts of search algo. This is based on pattern matching algo. You can try this one once.
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
static class Extensions
{
public static void Sanitize(this string item)
{
Regex rgx = new Regex("[^a-AA-Z0-9 -]");
item = rgx.Replace(item, " ");
}
public static string GetPipedString(this string item)
{
StringBuilder builder = new StringBuilder();
item.Split(' ').ToList().ForEach(x => builder.Append('|').Append(x));
builder.Remove(0, 1);
return builder.ToString();
}
public static IEnumerable<Person> FindPlayers(this IEnumerable<Person> persons, string searchKey)
{
searchKey.Sanitize();
string pattern = string.Format(#"^?:{0}\w*$", searchKey.GetPipedString());
return persons.Where(x => Regex.IsMatch(
string.Join(string.Empty,
new List<string>() { x.FirstName, x.LastName }),
pattern,
RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace));
}
}
class Program
{
static void Main(string[] args)
{
/* Assuming peoples is the IEnumerable<Person>.
Anyways there is an explicit sanitization of the string to remove the non alphanum characters*/
var items = peoples.FindPlayers("ANY DATA SPACE SEPARATED").ToList();
}
}

How do i create my own DSL on dotnet core, are there frameworks?

Sorry if my title is way off, but i have no clue where to start on this.
I came up with a little format that allow me to write some small steps in a json file. People knowing azure resource templates, its very much inspired by this.
{
"steps": [
{
"command": "mpc-info -i [laz input directory] -c [number processes]",
"outputs": {
"AABB": "[split(regex(stdout,\"\\('AABB: ', (.*?)\\)\",$1))]"
}
}
]
}
Where to i start if i want to write my own parser for those "[]" strings in the document?
Goal being that its easy for me to add new functions / variables that can be used to write expressions. I would like to define all this in c# code running on dotnet core netstandard 2.0.
So in the specifc instance above, a host program will run the command and produce some output on stdout. I now need my code to parse the output string
[split(regex(stdout,\"\\('AABB: ', (.*?)\\)\",$1))] and translate that into code that will run a Regex match on the variable stdout and return the first capture group as a argument to a split function that will create an array object of the items and replacing at the end the string in the JToken object.
Any pointers on what to read up or sample code that could get it started.
My naiv approch would just be write some studid code that do some search and replace and solve this small example, but what if i want to evolve my small framework with more functions ect.
Where do I start?
With a bit of help from twitter friends i managed to solve the problem using Sprache.
stdout.txt
Completed 100.00%!()
('AABB: ', 480000, 6150000, -1580, 530000, 6200000, 755)
('#Points:', 20517377941)
('Average density [pts / m2]:', 8.2069511764)
('Suggested number of tiles: ', 16.0)
('Suggested Potree-OctTree CAABB: ', 480000, 6150000, -1580, 530000, 6200000, 48420)
('Suggested Potree-OctTree spacing: ', 205.0)
('Suggested Potree-OctTree number of levels: ', 11)
Suggested potreeconverter command:
$(which PotreeConverter) -o <potree output directory> -l 11 -s 205 --CAABB "480000 6150000 -1580 530000 6200000 48420" --output-format LAZ -i <laz input directory>
Finished in 7.63 seconds
and a unit test that shows how it work
[TestMethod]
public void TestMethod1()
{
var parser = new ExpressionParser();
parser.Functions["add"] = (arguments) =>
arguments.Aggregate(0.0, (acc, argument) => acc + argument.ToObject<double>());
parser.Functions["split"] = (arguments) => JArray.FromObject(arguments.Single().ToString().Split(","));
parser.Functions["regex"] = RegexFunc;
Assert.AreEqual(4.0, parser.Evaluate("[add(2,2)]"));
Assert.AreEqual(7.0, parser.Evaluate("[add(2,2,3)]"));
Assert.AreEqual(3.0, parser.Evaluate("[add(2,2,-1)]"));
Assert.AreEqual(4.0, parser.Evaluate("[add(2,2,0,0)]"));
var stdout = File.ReadAllText("stdout.txt");
var test = parser.Evaluate("[split(regex(\"testfoo\",\"test(.*)\",\"$1\"))]");
Assert.AreEqual("[\"foo\"]",test.ToString( Newtonsoft.Json.Formatting.None));
parser.Functions["stdout"] = (arguments) => stdout;
parser.Functions["numbers"] = (arguments) => new JArray(arguments.SelectMany(c => c).Select(c => double.Parse(c.ToString())));
var AABB = parser.Evaluate("[numbers(split(regex(stdout(2),\"\\('AABB: ', (.*?)\\)\",\"$1\")))]");
CollectionAssert.AreEqual(new[] { 480000, 6150000, -1580, 530000, 6200000, 755 }, AABB.ToObject<int[]>());
}
and the actual implementation
public class ConstantEvaluator : IJTokenEvaluator
{
private string k;
public ConstantEvaluator(string k)
{
this.k = k;
}
public JToken Evaluate()
{
return JToken.Parse(k);
}
}
public class DecimalConstantEvaluator : IJTokenEvaluator
{
private decimal #decimal;
public DecimalConstantEvaluator(decimal #decimal)
{
this.#decimal = #decimal;
}
public JToken Evaluate()
{
return JToken.FromObject(#decimal);
}
}
public class StringConstantEvaluator : IJTokenEvaluator
{
private string text;
public StringConstantEvaluator(string text)
{
this.text = text;
}
public JToken Evaluate()
{
return text;
}
}
public interface IJTokenEvaluator
{
JToken Evaluate();
}
public class FunctionEvaluator : IJTokenEvaluator
{
private string name;
private IJTokenEvaluator[] parameters;
private ExpressionParser evaluator;
public FunctionEvaluator(ExpressionParser evaluator, string name, IJTokenEvaluator[] parameters)
{
this.name = name;
this.parameters = parameters;
this.evaluator = evaluator;
}
public JToken Evaluate()
{
return evaluator.Evaluate(name, parameters.Select(p => p.Evaluate()).ToArray());
}
}
public class ExpressionParser
{
public readonly Parser<IJTokenEvaluator> Function;
public readonly Parser<IJTokenEvaluator> Constant;
private static readonly Parser<char> DoubleQuote = Parse.Char('"');
private static readonly Parser<char> Backslash = Parse.Char('\\');
private static readonly Parser<char> QdText =
Parse.AnyChar.Except(DoubleQuote);
private static readonly Parser<char> QuotedPair =
from _ in Backslash
from c in Parse.AnyChar
select c;
private static readonly Parser<StringConstantEvaluator> QuotedString =
from open in DoubleQuote
from text in QdText.Many().Text()
from close in DoubleQuote
select new StringConstantEvaluator(text);
public Dictionary<string, Func<JToken[], JToken>> Functions { get; set; } = new Dictionary<string, Func<JToken[], JToken>>();
private readonly Parser<IJTokenEvaluator> Number = from op in Parse.Optional(Parse.Char('-').Token())
from num in Parse.Decimal
from trailingSpaces in Parse.Char(' ').Many()
select new DecimalConstantEvaluator(decimal.Parse(num) * (op.IsDefined ? -1 : 1));
public ExpressionParser()
{
Function = from name in Parse.Letter.AtLeastOnce().Text()
from lparen in Parse.Char('(')
from expr in Parse.Ref(() => Function.Or(Number).Or(QuotedString).Or(Constant)).DelimitedBy(Parse.Char(',').Token())
from rparen in Parse.Char(')')
select CallFunction(name, expr.ToArray());
Constant = Parse.LetterOrDigit.AtLeastOnce().Text().Select(k => new ConstantEvaluator(k));
}
public JToken Evaluate(string name, params JToken[] arguments)
{
return Functions[name](arguments);
}
IJTokenEvaluator CallFunction(string name, IJTokenEvaluator[] parameters)
{
return new FunctionEvaluator(this, name, parameters);
}
public JToken Evaluate(string str)
{
var stringParser = //Apparently the name 'string' was taken...
from first in Parse.Char('[')
from text in this.Function
from last in Parse.Char(']')
select text;
var func = stringParser.Parse(str);
return func.Evaluate();
}
}

C# Search multiline string for keyword and store variables

I have a multi-line string that I get from a database.
This string would have a format like:
The below text is for the label program
COMPANY=ComanyName
PRODUCT=ProductName
SERIALMASK=123456789YYWWXXXX
How do I go through this text and store variables or an array with ComanyName, ProductName, 123456789YYWWXXXX, so I can insert these values into textboxes on my Windows Forms Application?
My big hurdle is that sometimes the format would be:
The below text is for the label program
Company1 Information:
COMPANY=ComanyName
PRODUCT=ProductName
SERIALMASK=123456789YYWWXXXX
Company2 Information:
COMPANY=ComanyName
PRODUCT=ProductName
SERIALMASK=123456789YYWWXXXX
And in that case, I only wanna extract the first occurance of COMPANY, PRODUCT and SERIALMASK variables.
Now I have code that save each line in a variables, and I guess I could run a switch-case function in the foreach loop and look for substring. But I am hoping there is a more effective way
Try code below
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input =
"COMPANY=ComanyName\n" +
"PRODUCT=ProductName\n" +
"SERIALMASK=123456789YYWWXXXX\n";
string pattern = #"(?'name'\w+)=(?'value'\w+)";
MatchCollection matches = Regex.Matches(input,pattern);
foreach(Match match in matches)
{
Console.WriteLine("Name : '{0}', Value : '{1}'", match.Groups["name"].Value, match.Groups["value"].Value);
}
Console.ReadLine();
Dictionary<string, string> dict = matches.Cast<Match>()
.GroupBy(x => x.Groups["name"].Value, y => y.Groups["value"].Value)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}
This might do the trick for you
string allText = File.ReadAllText("JsonFound.txt");
List<string> allrecord = allText.Split(new string[] { "\r\n\r\n" }, StringSplitOptions.RemoveEmptyEntries)
.Where(x => x.Contains(":"))
.ToList();
List<CompanyInfo> CompanyInfos = new List<CompanyInfo>();
List<string> infos = new List<string>();
foreach(string s in allrecord)
{
infos = s.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
.Skip(Math.Max(0, 1))
.SelectMany(q=>q.Split('='))
.ToList();
CompanyInfo ci = new CompanyInfo();
ci.CompanyName = infos[1];
ci.ProductName = infos[3];
ci.SerialMaskNumber = infos[5];
CompanyInfos.Add(ci);
}
The Class CompanyInfo would look like this
public class CompanyInfo
{
public string CompanyName
{
get;
set;
}
public string ProductName
{
get;
set;
}
public string SerialMaskNumber
{
get;
set;
}
}
you can use the HashSet function. The list contains no duplicate records.
var hashSet = new HashSet<CompanyInfo>(CompanyInfos);

RegEx Find and Replace in Sublime Text

So I have a large file with the following types of lines:
public static string SyncButton = StringsProxy.GetStringByKey ("SyncButton") ?? "SYNC";
I need to replace this with the following version
public static string SyncButton {
get {
if (_SyncButton == null) {
_SyncButton = StringsProxy.GetStringByKey ("SyncButton") ?? "SYNC";
}
return _SyncButton;
}
set {
_SyncButton = value;
}
}
Can someone help construct a regular expression that could do a find and replace like this?
Replace this thing as regex:
public static string (.+?) = StringsProxy.GetStringByKey \("(.+?)"\) \?\? "(.+?)";
with this one:
public static string \1 {
get {
if (_\1 == null) {
_\1 = StringsProxy.GetStringByKey ("\1") ?? "\3";
}
return _\1;
}
set {
_\1 = value;
}
}
Example:
!
try this
Find What:
public static string ([^= ]*) (= StringsProxy\.GetStringByKey \("SyncButton"\) \?\? "SYNC";)
Replace With:
public static string \1 {
get {
if (_\1 == null) {
_\1 = \2
}
return _\1;
}
set {
_\1 = value;
}
}

searching an arraylist

I have a arraylist in my web application project in asp.net/C#/VS2008 and I'm using .net 3.5
I'm adding contents to the arraylist using a class which is defined as follows:
using System.Web;
class ShoppingCartDataStore
{
private string componentName;
private string componentPrice;
private string componentFileSize;
private string componentDescription;
public ShoppingCartDataStore(string componentName, string componentPrice, string componentFileSize, string componentDescription){
this.componentName = componentName;
this.componentPrice = componentPrice;
this.componentFileSize = componentFileSize;
this.componentDescription = componentDescription;
}
public string ComponentName
{
get
{
return this.componentName;
}
}
public string ComponentPrice
{
get
{
return this.componentPrice;
}
}
public string ComponentFileSize
{
get
{
return this.componentFileSize;
}
}
public string ComponentDescription
{
get
{
return this.componentDescription;
}
}
}
and I'm adding contents to the arraylist by following code:
ArrayList selectedRowItems = new ArrayList();
selectedRowItems.Add(new ShoppingCartDataStore(componentName, componentPrice, fileSize, componentDescription));
Suppose I want to search this arraylist after adding few values in this manner with componentName as the key. I tried the following code but I'm just not able to find a way to do this:
ArrayList temporarySelectedItemsList = new ArrayList();
ArrayList presentValue = new ArrayList();
string key = componentName; //some specific component name
temporarySelectedItemsList = selectedRowItems;
for (int i = 0; i < temporarySelectedItemsList.Count; i++)
{
presentValue = (ArrayList)temporarySelectedItemsList[i];
}
var results = selectedRowItems.OfType<ShoppingCartDataStore>().Where(x=>x.ComponentName == "foo")
of course you could get rid of the OfType if you were using a generic list rather than a arraylist
EDIT: So, I have no idea why you would NOT use LINQ or generics if you are in 3.5. But if you must:
ArrayList results = new ArrayList();
foreach (ShoppingCartDataStore store in selectedRowItems)
{
if(store.ComponentName == "foo"){
results.Add(store);
}
}
I'm sick and this is untested, but I think it'll work. :)
List<ShoppingCartDataStore> aList = new List<ShoppingCartDataStore>();
// add your data here
string key = componentName; //some specific component name
// Now search
foreach (ShoppingCartDataStore i in aList)
{
if (i.ComponentName == key)
{
// Found, do something
}
}

Categories