comparison string object to string array...

D

David Lazos

Hi All,

I use Contains method of String object to determine if a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************

supposing that, there is a string array like

string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};

Is it possible to determine if any string in this array occurs in a String
Object without using foreach or for patterns ?

Thanks in advance..
 
J

Jon Skeet [C# MVP]

I use Contains method of String object to determine if  a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************

supposing that,  there is a string array like

string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};

Is it possible to determine if any string in this array occurs in a String
Object  without using foreach or for patterns ?

If you're using C# 3 and .NET 3.5, you could do:

if (aryIPAddres.Any(candidate => candidate.Contains("127.0.0")))

(You might want to use StartsWith instead of Contains.)

Of course, this still uses for/foreach internally - if you can't
use .NET 3.5 you can easily write your own method to do it.

Jon
 
P

Pavel Minaev

I use Contains method of String object to determine if  a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************
supposing that,  there is a string array like
string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};
Is it possible to determine if any string in this array occurs in a String
Object  without using foreach or for patterns ?

If you're using C# 3 and .NET 3.5, you could do:

if (aryIPAddres.Any(candidate => candidate.Contains("127.0.0")))

If I understand the original poster correctly, he actually wanted to
do it the other way around, that is:

aryIPAddress.Any(candidate => ipAddress.Contains(candidate))

Still, for a sufficiently large list of substrings to match, it's not
so good - since a lookup from beginning to end of the string is done
for every element of the list. In this case, I'd recommend converting
the list of substrings to a Regex, and then use Regex.Match:

var regex = new Regex(string.Join("|", aryIPAddres.Select(s =>
Regex.Escape(s)).ToArray()));
if (regex.IsMatch(ipAddress)) { ... }

It will be somewhat faster, because it will reduce the list of
candidate substrings as it matches - e.g. as soon as it sees that the
first digit is "2", it will not even bother trying to match all IPs
that start with anything else.
 
J

Jon Skeet [C# MVP]

If I understand the original poster correctly, he actually wanted to
do it the other way around, that is:

aryIPAddress.Any(candidate => ipAddress.Contains(candidate))

That's very possible - I wasn't entirely sure which way round it was
meant to be.
Still, for a sufficiently large list of substrings to match, it's not
so good - since a lookup from beginning to end of the string is done
for every element of the list. In this case, I'd recommend converting
the list of substrings to a Regex, and then use Regex.Match:

var regex = new Regex(string.Join("|", aryIPAddres.Select(s =>
Regex.Escape(s)).ToArray()));
if (regex.IsMatch(ipAddress)) { ... }

It will be somewhat faster, because it will reduce the list of
candidate substrings as it matches - e.g. as soon as it sees that the
first digit is "2", it will not even bother trying to match all IPs
that start with anything else.

On the other hand, it has to join all the strings together to start
with, escape them all, and then parse the generated regular
expression.

In addition, the code is considerably more complicated to understand.

I'd certainly go for the simpler way to start with, and then do
concrete performance tests before changing to a regular expression
form.

Of course, if StartsWith is okay, that takes away the inefficiency
anyway :)

Jon
 
G

Göran Andersson

David said:
Hi All,

I use Contains method of String object to determine if a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************

supposing that, there is a string array like

string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};

Is it possible to determine if any string in this array occurs in a String
Object without using foreach or for patterns ?

Thanks in advance..

If the array is sorted (as in your example), you could use a binary
search and a custom comparer, which would be considerably faster than
looping through all the strings.

Something like:

if (Array.BinarySearch(aryIPAddress, ip, new ContainsComparer()) >= 0) {
// ip address found
}

public class ContainsComparer : IComparer<string> {

public int Compare(string x, string y) {
if (x.Contains(y) /*or perhaps the other way around*/ ) {
return 0;
} else {
return string.Compare(x, y);
}
}

}
 
P

Pavel Minaev

On the other hand, it has to join all the strings together to start
with, escape them all, and then parse the generated regular
expression.

Oh yes, it largely depends on the specific use case. If those IPs come
from some sort of a config file, and don't change at all (unless the
user explicitly requests config reload, maybe), but are often matched
against a large stream of IP addresses, then regex solution makes more
sense, and then it is probably worth to compile the regex to IL as
well - and this is the kind of scenario I had in mind (HTTP server
matching IPs of incoming requests against preconfigured whitelist/
blacklist which rarely changes, for example). If list of IP masks to
match changes often, then the simple solution will likely be faster.
 
P

Pavel Minaev

David said:
I use Contains method of String object to determine if  a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************
supposing that,  there is a string array like
string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};
Is it possible to determine if any string in this array occurs in a String
Object  without using foreach or for patterns ?
Thanks in advance..

If the array is sorted (as in your example), you could use a binary
search and a custom comparer, which would be considerably faster than
looping through all the strings.

Something like:

if (Array.BinarySearch(aryIPAddress, ip, new ContainsComparer()) >= 0) {
   // ip address found

}

public class ContainsComparer : IComparer<string> {

   public int Compare(string x, string y) {
     if (x.Contains(y) /*or perhaps the other way around*/ ) {
       return 0;
     } else {
       return string.Compare(x, y);
     }
   }

}

This won't work, because you'd have to have the array sorted using the
same Comparer as well, and it does not provide a total ordering - for
example, for a given pair of strings A="foobar", B="foo", this
comparer can simultaneously say that A==B, and B<A. To illustrate,
here's a sample set where it will go wrong:

{ "baz", "foo", "foobar" }

A binary search for "bar" using the provided comparer would go like
this:

1. { "baz", "foo", "foobar" }. Pick middle element - "foo". Compare:
"bar" < "foo". Take first half - { "baz" }
2. { "baz" }. Pick middle element - "baz". Compare: "bar" < "baz".
Take first half - { }.
3. { }. Empty set, search unsuccessful.

Whereas, obviously, "foobar" contains "bar", and a proper search would
have found it.
 
G

Göran Andersson

Pavel said:
David said:
Hi All,
I use Contains method of String object to determine if a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************
supposing that, there is a string array like
string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};
Is it possible to determine if any string in this array occurs in a String
Object without using foreach or for patterns ?
Thanks in advance..
If the array is sorted (as in your example), you could use a binary
search and a custom comparer, which would be considerably faster than
looping through all the strings.

Something like:

if (Array.BinarySearch(aryIPAddress, ip, new ContainsComparer()) >= 0) {
// ip address found

}

public class ContainsComparer : IComparer<string> {

public int Compare(string x, string y) {
if (x.Contains(y) /*or perhaps the other way around*/ ) {
return 0;
} else {
return string.Compare(x, y);
}
}

}

This won't work, because you'd have to have the array sorted using the
same Comparer as well, and it does not provide a total ordering - for
example, for a given pair of strings A="foobar", B="foo", this
comparer can simultaneously say that A==B, and B<A. To illustrate,
here's a sample set where it will go wrong:

{ "baz", "foo", "foobar" }

A binary search for "bar" using the provided comparer would go like
this:

1. { "baz", "foo", "foobar" }. Pick middle element - "foo". Compare:
"bar" < "foo". Take first half - { "baz" }
2. { "baz" }. Pick middle element - "baz". Compare: "bar" < "baz".
Take first half - { }.
3. { }. Empty set, search unsuccessful.

Whereas, obviously, "foobar" contains "bar", and a proper search would
have found it.

You are correct, it only works if the comparer uses StartsWith instead
of Contains, which by the look of it might be what the OP is actually
looking for.
 
G

Göran Andersson

David said:
Hi All,

I use Contains method of String object to determine if a string variable
has another string, like that.
***************************
ipAddress.Contains("127.0.0")
****************************

supposing that, there is a string array like

string[] aryIPAddress = {"127.0.0.", "192.168.1.","66.249.71."};

Is it possible to determine if any string in this array occurs in a String
Object without using foreach or for patterns ?

Thanks in advance..

Another alternative is to not compare strings at all.

Create an IpRange class that has an upper and lower limit for an ip
range, e.g. 127.0.0.0 - 127.0.0.255. An ip address (at least in ip4) can
be represented as an uint, so checking if an ip address is within the
range would be two integer comparisons. Much more efficient than
comparing strings.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top