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);
Related
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();
}
}
When I am trying to filter records that match any item in the search list then it's not returning me anything. please see code
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
class Test {
// Main Method
public static void Main(String[] args)
{
List<String> searchlist = new List<String>();
searchlist.Add("Mang");
searchlist.Add("Apple");
List<String> firstlist = new List<String>();
firstlist.Add("Mango");
firstlist.Add("Apple");
firstlist.Add("Orange");
firstlist.Add("Grapes");
test1 obj = new test1();
obj.stringList = firstlist;
Console.Write(obj.stringList.Where(x=> searchlist.Contains(x)).Count());
}
public class test1
{
public string id { get; set; }
public string name { get; set; }
public List<String> stringList { get; set; }
}
}
In the above example, if I will pass a full string like "Mango" then it will return the result but if I try to search only "Mang" (partial words) then it's not working.
The reason is that when using Contains() on a collection, it results in comparing its items. So you are asking whether "Mang" == "Mango"
As it was stated here in another answer, you want to ensure that strings are compared using contains, but it is important to choose which string we apply a Contains to
var result = obj.stringList.Where(item => searchlist.Any(searchString => item.Contains(searchString))).Count();
Try this
Console.Write(obj.stringList.Where(x => searchlist.Any(list => x.Contains(list))).Count());
i want to query data from a mongoDB database and want to apply a filter to it. Stuff like this works fine:
var wantedAttributes = "word";
Collection.Find(Builders<MyModel>.Filter.Eq("Attributes", wantedAttributes)).ToList();
but only if my wantedAttributes match exactly to the Attributes field value in the db.
My usecase is that, the Attributes values are lists of strings, like:
word1, word2, word3
word2, word3, word1
word3, word1, word4
What i want is a method to get or match all entries that contain a given set of words, but not necessary in the same order. More words are allowed but not less!
So if my wantedAttributes = word4 i want to get the third entry only and if my wantedAttributes = word1,word2 i want the first and the second.
The wantedAttributes do not necessary has to be a string of comma separated words, but the database entries are.
What is the best way to achieve that?
Try this:
var wanteds = "word4";
var filter = Builders<MyModel>.Filter.Empty;
foreach (var wanted in wanteds.Split(','))
{
filter = filter & Builders<MyModel>.Filter.Where(m => m.Str.Contains(wanted.Trim()));
}
var models = collection.Find(filter).ToList();
My model:
class MyModel
{
[BsonElement("id")]
public int Id { get; set; }
[BsonElement("str")]
public string Str { get; set; }
}
You can customize the wanted string and string separator.
since you asked the best way, i'd suggest storing the attributes as a string array instead of a string in the db and query like so:
var wantedAttributes = new[] { "word1", "word2" };
var result = collections.AsQueryable<MyModel>()
.Where(m => wantedAttributes.All(a => m.Attributes.Contains(a)))
.ToList();
this way you can index the Attributes field and get lightning fast results. the m.Str.Contains(wanted.Trim()) method which #charles suggested will cause a regex match which will not be able to use an index. only prefixed regex queries can use indexes in mongodb.
here's full test program:
using MongoDB.Entities;
using MongoDB.Entities.Core;
using System;
using System.Linq;
namespace StackOverFlow
{
public class MyModel : Entity
{
public string[] Attributes { get; set; }
}
public static class Program
{
private static void Main()
{
new DB("test");
new[] {
new MyModel{
Attributes = new[]{ "word1", "word2", "word3" }
},
new MyModel{
Attributes = new[]{ "word2", "word3", "word1" }
},
new MyModel{
Attributes = new[]{ "word3", "word1", "word4" }
}
}
.Save();
var wantedAttributes = new[] { "word1", "word2" };
var result = DB.Queryable<MyModel>()
.Where(m => wantedAttributes.All(a => m.Attributes.Contains(a)))
.ToList();
}
}
}
I'm looking for an efficient way of sorting an array of email addresses to avoid items with the same domain to be consecutive, in C#.
Email addresses inside the array are already distinct and all of them are lower case.
Example:
Given an array with the following entries:
john.doe#domain1.com
jane_doe#domain1.com
patricksmith#domain2.com
erick.brown#domain3.com
I would like to obtain something similar to the following:
john.doe#domain1.com
patricksmith#domain2.com
jane_doe#domain1.com
erick.brown#domain3.com
With the help of an extension method (stolen from https://stackoverflow.com/a/27533369/172769), you can go like this:
List<string> emails = new List<string>();
emails.Add("john.doe#domain1.com");
emails.Add("jane_doe#domain1.com");
emails.Add("patricksmith#domain2.com");
emails.Add("erick.brown#domain3.com");
var q = emails.GroupBy(m => m.Split('#')[1]).Select(g => new List<string>(g)).Interleave();
The Interleave method is defined as:
public static IEnumerable<T> Interleave<T>(this IEnumerable<IEnumerable<T>> source )
{
var queues = source.Select(x => new Queue<T>(x)).ToList();
while (queues.Any(x => x.Any())) {
foreach (var queue in queues.Where(x => x.Any())) {
yield return queue.Dequeue();
}
}
}
So basically, we create groups based on the domain part of the email adresses, project (or Select) each group into a List<string>, and then "Interleave" those lists.
I have tested against your sample data, but more thorough testing might be needed to find edge cases.
DotNetFiddle snippet
Cheers
This will distribute them semi-evenly and attempt to avoid matching domains next to each other (although in certain lists that may be impossible). This answer will use OOP and Linq.
DotNetFiddle.Net Example
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var seed = new List<string>()
{
"1#a.com",
"2#a.com",
"3#a.com",
"4#a.com",
"5#a.com",
"6#a.com",
"7#a.com",
"8#a.com",
"9#a.com",
"10#a.com",
"1#b.com",
"2#b.com",
"3#b.com",
"1#c.com",
"4#b.com",
"2#c.com",
"3#c.com",
"4#c.com"
};
var work = seed
// Create a list of EmailAddress objects
.Select(s => new EmailAddress(s)) // s.ToLowerCase() ?
// Group the list by Domain
.GroupBy(s => s.Domain)
// Create a List<EmailAddressGroup>
.Select(g => new EmailAddressGroup(g))
.ToList();
var currentDomain = string.Empty;
while(work.Count > 0)
{
// this list should not be the same domain we just used
var noDups = work.Where(w => w.Domain != currentDomain);
// if none exist we are done, or it can't be solved
if (noDups.Count() == 0)
{
break;
}
// find the first group with the most items
var workGroup = noDups.First(w => w.Count() == noDups.Max(g => g.Count()));
// get the email address and remove it from the group list
var workItem = workGroup.Remove();
// if the group is empty remove it from *work*
if (workGroup.Count() == 0)
{
work.Remove(workGroup);
Console.WriteLine("removed: " + workGroup.Domain);
}
Console.WriteLine(workItem.FullEmail);
// last domain looked at.
currentDomain = workItem.Domain;
}
Console.WriteLine("Cannot disperse email addresses affectively, left overs:");
foreach(var workGroup in work)
{
while(workGroup.Count() > 0)
{
var item = workGroup.Remove();
Console.WriteLine(item.FullEmail);
}
}
}
public class EmailAddress
{
public EmailAddress(string emailAddress)
{
// Additional Email Address Validation
var result = emailAddress.Split(new char[] {'#'}, StringSplitOptions.RemoveEmptyEntries)
.ToList();
if (result.Count() != 2)
{
new ArgumentException("emailAddress");
}
this.FullEmail = emailAddress;
this.Name = result[0];
this.Domain = result[1];
}
public string Name { get; private set; }
public string Domain { get; private set; }
public string FullEmail { get; private set; }
}
public class EmailAddressGroup
{
private List<EmailAddress> _emails;
public EmailAddressGroup(IEnumerable<EmailAddress> emails)
{
this._emails = emails.ToList();
this.Domain = emails.First().Domain;
}
public int Count()
{
return _emails.Count();
}
public string Domain { get; private set; }
public EmailAddress Remove()
{
var result = _emails.First();
_emails.Remove(result);
return result;
}
}
}
Output:
1#a.com
1#b.com
2#a.com
1#c.com
3#a.com
2#b.com
4#a.com
2#c.com
5#a.com
3#b.com
6#a.com
3#c.com
7#a.com
removed: b.com
4#b.com
8#a.com
removed: c.com
4#c.com
9#a.com
Cannot disperse email addresses affectively, left overs:
10#a.com
Something like this will spread them equally, but you will have the problems (=consecutive elements) at the end of the new list...
var list = new List<string>();
list.Add("john.doe#domain1.com");
list.Add("jane_doe#domain1.com");
list.Add("patricksmith#domain2.com");
list.Add("erick.brown#domain3.com");
var x = list.GroupBy(content => content.Split('#')[1]);
var newlist = new List<string>();
bool addedSomething=true;
int i = 0;
while (addedSomething) {
addedSomething = false;
foreach (var grp in x) {
if (grp.Count() > i) {
newlist.Add(grp.ElementAt(i));
addedSomething = true;
}
}
i++;
}
Edit: Added a high level description :)
What this code does is group each element by the domain, sort the groups by size in descending order (largest group first), project the elements of each group into a stack, and pop them off of each stack (always pop the next element off the largest stack with a different domain). If there is only a single stack left, then its contents are yielded.
This should make sure that all domains distributed as evenly as possible.
MaxBy extension method from: https://stackoverflow.com/a/31560586/969962
private IEnumerable<string> GetNonConsecutiveEmails(List<string> list)
{
var emailAddresses = list.Distinct().Select(email => new EmailAddress { Email = email, Domain = email.Split('#')[1]}).ToArray();
var groups = emailAddresses
.GroupBy(addr => addr.Domain)
.Select (group => new { Domain = group.Key, EmailAddresses = new Stack<EmailAddress>(group)})
.ToList();
EmailAddress lastEmail = null;
while(groups.Any(g => g.EmailAddresses.Any()))
{
// Try and pick from the largest stack.
var stack = groups
.Where(g => (g.EmailAddresses.Any()) && (lastEmail == null ? true : lastEmail.Domain != g.Domain))
.MaxBy(g => g.EmailAddresses.Count);
// Null check to account for only 1 stack being left.
// If so, pop the elements off the remaining stack.
lastEmail = (stack ?? groups.First(g => g.EmailAddresses.Any())).EmailAddresses.Pop();
yield return lastEmail.Email;
}
}
class EmailAddress
{
public string Domain;
public string Email;
}
public static class Extensions
{
public static T MaxBy<T,U>(this IEnumerable<T> data, Func<T,U> f) where U:IComparable
{
return data.Aggregate((i1, i2) => f(i1).CompareTo(f(i2))>0 ? i1 : i2);
}
}
What I am trying to do here is to sort them first.
Then I re-arrange from a different end. I'm sure there're more efficient ways to do this but this is one easy way to do it.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
String[] emails = { "john.doe#domain1.com", "jane_doe#domain1.com", "patricksmith#domain2.com", "erick.brown#domain3.com" };
var result = process(emails);
}
static String[] process(String[] emails)
{
String[] result = new String[emails.Length];
var comparer = new DomainComparer();
Array.Sort(emails, comparer);
for (int i = 0, j = emails.Length - 1, k = 0; i < j; i++, j--, k += 2)
{
if (i == j)
result[k] = emails[i];
else
{
result[k] = emails[i];
result[k + 1] = emails[j];
}
}
return result;
}
}
public class DomainComparer : IComparer<string>
{
public int Compare(string left, string right)
{
int at_pos = left.IndexOf('#');
var left_domain = left.Substring(at_pos, left.Length - at_pos);
at_pos = right.IndexOf('#');
var right_domain = right.Substring(at_pos, right.Length - at_pos);
return String.Compare(left_domain, right_domain);
}
}
}
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.