Search Array

  • Thread starter Thread starter Jonathan Wood
  • Start date Start date
J

Jonathan Wood

I've created a simple array of unsorted strings.

Is there a SIMPLE way to see if the array contains a string while ignoring
case, without writing my own loop?

I don't mind writing my own loop but if there's already something to do
this, I'd like to find it. I've found all sorts of complex stuff but nothing
to perform this rather trivial task.

Thanks.

Jonathan
 
It is a rather trivial task. Any particular reason you want to avoid the
extra few lines required for an explicit loop?

Because it appears there is literally tons of stuff related to searching
arrays and, since I am shifting my development efforts to C# it occurs to me
that I may want to learn how this should be done in C#.

Other than that, it would be nice to keep the code in my ASPX page smaller
so it is slightly easier to read. (I could go on with other considerations
as well, but will stop there.)
You can use Array.Find<T>() or Enumerable.Contains<TSource>(). Either of
those will allow you to provide a predicate that does the exact comparison
you want (Array.Find<T>() is probably easier, since it allows you to
provide the predicate directly, rather than wrapping it in an interface
implementation).

I'm completely new to Predicates. The only example I could find did not
provide a means of specifying the object I was looking for. Instead, it
simply compared each item to some hard-coded rules. Anyone have a link that
shows how I'd do this?
But loops aren't exactly evil or anything. It wouldn't be the end of the
world if you had to write one yourself. :)

I've written more loops over the past 23 years than you can shake a stick
at. I can do it again, but I'd prefer to try and fully understand the
language.

Thanks.

Jonathan
 
Jonathan said:
I've created a simple array of unsorted strings.

Is there a SIMPLE way to see if the array contains a string while
ignoring case, without writing my own loop?

I don't mind writing my own loop but if there's already something to do
this, I'd like to find it. I've found all sorts of complex stuff but
nothing to perform this rather trivial task.

A loop is necessary. But you can avoid writing it.

See code below (assumes .NET 3.5).

Arne

=====================================

using System;

namespace E
{
public class Program
{
public static void Main(string[] args)
{
string[] sa = { "A", "B", "C" };
Console.WriteLine(Array.Exists(sa, s => String.Compare(s,
"B", true) == 0));
Console.WriteLine(Array.Exists(sa, s => String.Compare(s,
"b", true) == 0));
Console.WriteLine(Array.Exists(sa, s => String.Compare(s,
"X", true) == 0));
}
}
}
 
Thanks. However, it's sounding like these Predicates is what I really need
to learn more about.

Jonathan

Arne Vajhøj said:
Jonathan said:
I've created a simple array of unsorted strings.

Is there a SIMPLE way to see if the array contains a string while
ignoring case, without writing my own loop?

I don't mind writing my own loop but if there's already something to do
this, I'd like to find it. I've found all sorts of complex stuff but
nothing to perform this rather trivial task.

A loop is necessary. But you can avoid writing it.

See code below (assumes .NET 3.5).

Arne

=====================================

using System;

namespace E
{
public class Program
{
public static void Main(string[] args)
{
string[] sa = { "A", "B", "C" };
Console.WriteLine(Array.Exists(sa, s => String.Compare(s, "B",
true) == 0));
Console.WriteLine(Array.Exists(sa, s => String.Compare(s, "b",
true) == 0));
Console.WriteLine(Array.Exists(sa, s => String.Compare(s, "X",
true) == 0));
}
}
}
 
Hi,

You might want to try LINQ. This allows You to declare what You would like
to do instead of telling how to do it :D
For example one can come up with this:
string result = yourStringArray.Where(s => s.Equals(searchCriteria,
StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

This will select the first string in the array for which the
"Equals(searchCriteria, StringComparison.CurrentCultureIgnoreCase)" is true
or null if no such string is in the array.

Hope You find this useful.
-Zsolt
 
If it's just about avoiding to write the code, you could use:

string[] myStrings = { "A", "B", "C" };
string stringToFind = "B";

if (Array.IndexOf(myStrings, stringToFind) >= 0)
doSomething();

It's oldschool and doesn't look as cool as LINQ and Lambda-Expressions,
but it's far easier to keep in mind... ;-)

HTH, Stefan
 
Did you look at the MSDN documentation for each method I mentioned?

There are code examples there, and each provides exactly for your code to
provide an object to compare against and a predicate used to do the
comparison.

I looked at the one for Array.Find<T>, the one you recommended as being
simpler, before your post. It is what I was talking about where the
predicate method searched using some hard-coded criteria, and the call Point
first = Array.Find(points, ProductGT10); does NOT include an argument that
described the object being sought.
With Enumerable.Contains<TSource>() you pass the object to compare against
to the method itself. With Array.Find<T>(), you have to embed the
reference to the object in code somehow.

The example on MSDN for Array.Find<T>() shows a hard-coded value, but for
someone who has been shaking sticks at loops for 23 years, it should not
be difficult to extrapolate to storing an object somewhere accessible for
the code to use in the comparison. This becomes even easier if you use an
anonymous method, because you can just refer to any variable accessibly by
the named method in which the anonymous method is declared in the
anonymous method itself as part of the comparison.

I'm just trying to learn the details of C# and .NET. I'm able to develop
what I need but have no idea what an anonymous method is and having to
implement an interface for such a trivial task doesn't sound like a good
choice to me.

Looking around, I see all sorts of stuff for searching collections including
BinarySearch, which is a somewhat less-basic operation. So why Find wasn't
designed to take an argument that says what it is you want to find is a bit
of a mystery to me.

Jonathan
 
Stefan said:
Jonathan said:
I've created a simple array of unsorted strings.

Is there a SIMPLE way to see if the array contains a string while
ignoring case, without writing my own loop?

I don't mind writing my own loop but if there's already something to
do this, I'd like to find it. I've found all sorts of complex stuff
but nothing to perform this rather trivial task.
If it's just about avoiding to write the code, you could use:

string[] myStrings = { "A", "B", "C" };
string stringToFind = "B";

if (Array.IndexOf(myStrings, stringToFind) >= 0)
doSomething();

It's oldschool and doesn't look as cool as LINQ and Lambda-Expressions,
but it's far easier to keep in mind...

I believe it is part of old school to note such things
as "ignoring case" !

Arne
 
That's the type of things I was looking for. Unfortunately, it does not meet
my requirements as it is case-sensitive.

Jonathan

Stefan L said:
If it's just about avoiding to write the code, you could use:

string[] myStrings = { "A", "B", "C" };
string stringToFind = "B";

if (Array.IndexOf(myStrings, stringToFind) >= 0)
doSomething();

It's oldschool and doesn't look as cool as LINQ and Lambda-Expressions,
but it's far easier to keep in mind... ;-)

HTH, Stefan



Jonathan said:
I've created a simple array of unsorted strings.

Is there a SIMPLE way to see if the array contains a string while
ignoring case, without writing my own loop?

I don't mind writing my own loop but if there's already something to do
this, I'd like to find it. I've found all sorts of complex stuff but
nothing to perform this rather trivial task.

Thanks.

Jonathan
 
A "predicate" is nothing more than a delegate used in a particular way.
Arne's reply demonstrates the use of a delegate, created via a lambda
expression. If in his example you had a local string variable named "s1"
and used that in place of "B", "b", and/or "X", that would demonstrate the
variable capturing I described in my other post.

My apology to Arne if I didn't grasp what he was describing. There is some
syntax I'm just not familiar with (such as s =>).

My loop has been written and, of course, works just fine. But may eliminate
it if I could use a C# construct. In the case, of Array.Exists, I don't get
the actual index, which I do need. Could you tell me if the same syntax
could be used with Array.Find?

Thanks.

Jonathan
 
Upps, you're totally right. I was kind of insensitive to the desired
case-insensitivity, I'd better read the question correctly.

Sorry...


Peter said:
If it's just about avoiding to write the code, you could use:

string[] myStrings = { "A", "B", "C" };
string stringToFind = "B";

if (Array.IndexOf(myStrings, stringToFind) >= 0)
doSomething();

It's oldschool and doesn't look as cool as LINQ and
Lambda-Expressions, but it's far easier to keep in mind... ;-)

But doesn't do a case-insensitive search. So, unfortunately, doesn't
actually address the original question.

Pete
 
Jonathan said:
My apology to Arne if I didn't grasp what he was describing. There is
some syntax I'm just not familiar with (such as s =>).

Array.Exists(sa, s => String.Compare(s, "B", true) == 0)

means:

check if within array sa exists an element where
String.Compare(s, "B", true) == 0 !
In the case, of
Array.Exists, I don't get the actual index, which I do need. Could you
tell me if the same syntax could be used with Array.Find?

In that case you need FindIndex with the same syntax.

Arne

=========================

using System;

namespace E
{
public class Program
{
public static void Main(string[] args)
{
string[] sa = { "A", "B", "C" };
Console.WriteLine(Array.FindIndex(sa, s =>
String.Compare(s, "B", true) == 0));
Console.WriteLine(Array.FindIndex(sa, s =>
String.Compare(s, "b", true) == 0));
Console.WriteLine(Array.FindIndex(sa, s =>
String.Compare(s, "X", true) == 0));
}
}
}
 
That's the type of things I was looking for. Unfortunately, it does not meet
my requirements as it is case-sensitive.

The simple reason why there's no case-insensitive Array.IndexOf (nor
IndexOf for any other collection) is that IndexOf is generic - it is
the same for numbers, strings, and pretty much anything. Of those,
only strings even have the notion of case-sensitivity, so a generic
method has no way to account for that.

Think of it that way. IndexOf uses the "generic equality" comparison
for whatever type you have an array of. What you want is a special
kind of string comparison. So, you need to provide your own comparison
predicate. And yes, you can do that with Find (or rather, FindIndex,
if what you want to get is the index of the string in the collection):

int indexOfFoo = Array.FindIndex(strings, s => "foo".Equals(s,
StringComparison.CurrentCultureIgnoreCase));

Plain Find (which returns the value found) can be used in the same way
as well. By the way, note that it does "foo".Equals(s) rather than
s.Equals("foo") - this is probably obvious, but it's just in case the
array has some null elements...
 
Cool. Thanks.

--
Jonathan Wood
SoftCircuits Programming
http://www.softcircuits.com
http://www.softcircuits.com/blog/

Arne Vajhøj said:
Jonathan said:
My apology to Arne if I didn't grasp what he was describing. There is
some syntax I'm just not familiar with (such as s =>).

Array.Exists(sa, s => String.Compare(s, "B", true) == 0)

means:

check if within array sa exists an element where
String.Compare(s, "B", true) == 0 !
In the case, of Array.Exists,
I don't get the actual index, which I do need. Could you tell me if the
same syntax could be used with Array.Find?

In that case you need FindIndex with the same syntax.

Arne

=========================

using System;

namespace E
{
public class Program
{
public static void Main(string[] args)
{
string[] sa = { "A", "B", "C" };
Console.WriteLine(Array.FindIndex(sa, s => String.Compare(s,
"B", true) == 0));
Console.WriteLine(Array.FindIndex(sa, s => String.Compare(s,
"b", true) == 0));
Console.WriteLine(Array.FindIndex(sa, s => String.Compare(s,
"X", true) == 0));
}
}
}
 
Yeah, I understand your point. Thanks.

Jonathan

That's the type of things I was looking for. Unfortunately, it does not
meet
my requirements as it is case-sensitive.

The simple reason why there's no case-insensitive Array.IndexOf (nor
IndexOf for any other collection) is that IndexOf is generic - it is
the same for numbers, strings, and pretty much anything. Of those,
only strings even have the notion of case-sensitivity, so a generic
method has no way to account for that.

Think of it that way. IndexOf uses the "generic equality" comparison
for whatever type you have an array of. What you want is a special
kind of string comparison. So, you need to provide your own comparison
predicate. And yes, you can do that with Find (or rather, FindIndex,
if what you want to get is the index of the string in the collection):

int indexOfFoo = Array.FindIndex(strings, s => "foo".Equals(s,
StringComparison.CurrentCultureIgnoreCase));

Plain Find (which returns the value found) can be used in the same way
as well. By the way, note that it does "foo".Equals(s) rather than
s.Equals("foo") - this is probably obvious, but it's just in case the
array has some null elements...
 
Thanks for the additional information.

Jonathan

Peter Duniho said:
I didn't say it did. I said the Array.Find<T>() method provides for your
code to provide an object to compare against. Sorry if my statement was
confusing to you.


http://social.Msdn.microsoft.com/Search/en-US/?query=c# anonymous method

There would be no need to implement an interface with Array.Find<T>(), but
without an anonymous method (or lambda expression, a sort of refinement of
anonymous methods), you would need to use a named method that had access
to the specific data it needed to use. If you knew for sure that you'd
only ever be executing this code in one thread, one search at a time, you
could just put the data in a class member of the class where the search
code resides and refer to it from an instance method you use for the
predicate.

If you couldn't guarantee that, you'd want to create a class containing
the data and with an instance method for the predicate so that you could
refer to the data from the method. That's very roughly what an anonymous
method does, just without all the typing.


With an anonymous method, there's no need. Besides, there are plenty of
other examples of search methods that don't take an object as an argument,
including many of the BinarySearch overloads. Frankly, the number of
times when passing the object as an argument makes the code _clearer_ and
_simpler_ is pretty small. I'm not surprised at all that that's not the
normal pattern, and instead is reserved only for the special cases where
it could be done in a way that supports the simplest use cases.

Pete
 
True, but LINQ is about readability. Consider using the LINQ expression
instead of the Enumerable extension method.

var strings = getStringArray();
var matchingStrings = from s in strings
where s.Equals("something I want",
StringComparison.CurrentCultureIgnoreCase)
select s;

And can even be used readably with regular expressions (for very flexible
searching of the string array):

var strings = getStringArray();
var re = new System.Text.RegularExpressions.Regex(@"\w+\s+\w+[45ab]");
var matchingStrings = from s in strings where re.IsMatch(s) select s;
 
Back
Top