Is there a function equivalent to "String.IndexOf(String[])"? - c#

Is there any function that returns the index of the first occurrence of ANY string in a given array like: String.IndexOf(String[])?
or do I need a custom function for it?
For example the below function
"AppleBananaCherry".IndexOf(new[] {"Banana", "Cherry"});
and
"AppleBananaCherry".IndexOf(new[] {"Cherry", "Banana"});
returns 5

There is no prepared function but you can use something like this,
var sample = "AppleBananaCherry";
var input = new[] { "Cherry", "Banana" };
var result = input.Min(x => sample.IndexOf(x));
If sample has not any item of input, it returns -1

This should
public static int IndexOf(this string s, string[] values) {
var found = values
.Select(v => s.IndexOf(v))
.Where(index => index >= 0)
.OrderBy(v => v)
.Take(1)
.ToList();
return found.Count > 0 ? found[0] : -1;
}
EDIT: Removing -1 values

There is no built-in function for that, String.IndexOf() method accepts only single string or char as parameter, but you can write your own extension method, which uses IndexOf for every item in array, like in the following sample. It also should correctly exclude -1 from intermediate result
public static class Ext
{
public static int IndexOf(this string thisString, string[] values)
{
var index = thisString.Length;
var isFound = false;
foreach (var item in values)
{
var itemIndex = thisString.IndexOf(item, StringComparison.InvariantCulture);
if (itemIndex != -1 && itemIndex < index)
{
index = itemIndex;
isFound = true;
}
}
return isFound ? index : -1;
}
}
The usage example
var index = "AppleBananaCherry".IndexOf(new[] {"Banana", "Cherry"}); //returns 5
index = "AppleBananaCherry".IndexOf(new[] { "Cherry", "Banana" }); //returns 5

Related

How to sort descending a string list contains strings with alphabets & numbers in C# .net?

I have values in a string list like
AB1001_A
AB1001_B
AB1002_2
AB1002_C
AB1003_0
AB1003_
AB1003_B
AB1003_A
AB1001_0
AB1001_1
AB1001_2
AB1001_C
AB1002_B
AB1002_A
And I wanted to sort this by ascending order and the suffixes in descending order like below
AB1001_2
AB1001_1
AB1001_0
AB1001_C
AB1001_B
AB1001_A
AB1002_0
AB1002_B
AB1002_A
AB1003_0
AB1003_B
AB1003_A
AB1003_
How can I code it in C#.net?
It is quite strange sorting, but if you really need it, try something like this:
List<string> lItemsOfYourValues = new List<string>() {"AB1001_A","AB1001_B","AB1001_0" /*and next your values*/};
List<Tuple<string,string,string>> lItemsOfYourProcessedValues = new List<Tuple<string,string,string>>();
string[] arrSplitedValue;
for(int i = 0; i < lItemsOfYourValues.Count; i++)
{
arrSplitedValue = lItemsOfYourValues[i].Split("_");
lItemsOfYourProcessedValues.add(new Tuple<string,string,string>(lItemsOfYourValues[i], arrSplitedValue[0], arrSplitedValue[1]));
}
List<string> lSortedValues = lItemsOfYourProcessedValues.OrderBy(o => o.Item2).ThenByDescending(o => o.Item3).Select(o => o.Item1).ToList();
It looks like you have an error in your expected results, since AB1002_2 is in the input but not in the expected results.
Assuming that's just an error, and further assuming that the suffixes are limited to a single character or digit, you can solve the sorting by writing a special comparer like so:
static int compare(string x, string y)
{
var xParts = x.Split('_', StringSplitOptions.RemoveEmptyEntries);
var yParts = y.Split('_', StringSplitOptions.RemoveEmptyEntries);
if (xParts.Length != yParts.Length)
return yParts.Length - xParts.Length; // No suffix goes after suffix.
if (xParts.Length == 0) // Should never happen.
return 0;
int comp = string.Compare(xParts[0], yParts[0], StringComparison.Ordinal);
if (comp != 0 || xParts.Length == 1)
return comp;
if (char.IsDigit(xParts[1][0]) && !char.IsDigit(yParts[1][0]))
return -1; // Digits go before non-digit.
if (!char.IsDigit(xParts[1][0]) && char.IsDigit(yParts[1][0]))
return 1; // Digits go before non-digit.
return string.Compare(yParts[1], xParts[1], StringComparison.Ordinal);
}
Which you can then use to sort a string list, array or IEnumerable<string>, like so:
using System;
using System.Collections.Generic;
using System.Linq;
namespace Demo
{
static class Program
{
static void Main()
{
var strings = new []
{
"AB1001_A",
"AB1001_B",
"AB1002_2",
"AB1002_C",
"AB1003_0",
"AB1003_",
"AB1003_B",
"AB1003_A",
"AB1001_0",
"AB1001_1",
"AB1001_2",
"AB1001_C",
"AB1002_B",
"AB1002_A",
};
static int compare(string x, string y)
{
var xParts = x.Split('_', StringSplitOptions.RemoveEmptyEntries);
var yParts = y.Split('_', StringSplitOptions.RemoveEmptyEntries);
if (xParts.Length != yParts.Length)
return yParts.Length - xParts.Length;
if (xParts.Length == 0)
return 0;
int comp = string.Compare(xParts[0], yParts[0], StringComparison.Ordinal);
if (comp != 0 || xParts.Length == 1)
return comp;
if (char.IsDigit(xParts[1][0]) && !char.IsDigit(yParts[1][0]))
return -1; // Digits go before non-digit.
if (!char.IsDigit(xParts[1][0]) && char.IsDigit(yParts[1][0]))
return 1; // Digits go before non-digit.
return string.Compare(yParts[1], xParts[1], StringComparison.Ordinal);
}
var stringList = strings.ToList();
stringList.Sort(compare);
Console.WriteLine("Sorted list:");
Console.WriteLine(string.Join("\n", stringList));
var stringArray = strings.ToArray();
Array.Sort(stringArray, compare);
Console.WriteLine("\nSorted array:");
Console.WriteLine(string.Join("\n", stringArray));
var sequence = strings.Select(element => element);
var sortedSeq = sequence.OrderBy(element => element, Comparer<string>.Create(compare));
Console.WriteLine("\nSorted sequence:");
Console.WriteLine(string.Join("\n", sortedSeq));
}
}
}
Try it online on .Net Fiddle
Finally I got the soln by this
var mystrings = new []
{
"AB1001_A",
"AB1001_B",
"AB1002_2",
"AB1002_C",
"AB1003_0",
"AB1003_",
"AB1003_B",
"AB1003_A",
"AB1001_0",
"AB1001_1",
"AB1001_2",
"AB1001_C",
"AB1002_B",
"AB1002_A",
};
mystrings.Cast<string>().OrderBy(x => PadNumbers(x));
and then PadNumbers function as like below
public static string PadNumbers(string input)
{
return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}

C# array for finding all the 2's

Basically what I have to do is find a certain number, which in this case is 2, and see how many times I have that number in my program, I assumed that I would have to use a .GetValue(42) but it's not doing it right, the code I am using is
static int count2(int[] input)
{
return input.GetValue(2);
}
input is from a separate method, but it contains the values that I'm working with which is
int [] input = {1,2,3,4,5};
Not sure if you count specifically the number 2, or any number that contains the number 2.
For the later here's the easy way:
public int count2(int[] input) {
int counter = 0;
foreach(var i in input) {
if (i.ToString().Contains("2"))
{
++counter;
}
}
return counter;
}
You can do it with LINQ
input.Count(x=>x==2);
Array.GetValue() "gets the value at the specified position in the one-dimensional Array" which is not what you want. (in your example it will return 3 because that's the value at index 2 of your array).
You want to count the number of times a specific item is in the array. That's a matter of looping and checking each item:
var counter = 0;
foreach(var item in input)
{
if(item == 2)
{
counter++;
}
}
return counter;
to get a count do this
int [] inputDupes = {1,2,3,4,5,2};
var duplicates = inputDupes
.Select(w => inputDupes.Contains(2))
.GroupBy(q => q)
.Where(gb => gb.Count() > 1)
.Select(gb => gb.Key).Count();//returns an Int32 value
to see if there are duplicates of the number 2 then do the following
int [] inputDupes = {1,2,3,4,5,2};
var duplicates = inputDupes
.Select(w => inputDupes.Contains(2))
.GroupBy(q => q)
.Where(gb => gb.Count() > 1)
.Select(gb => gb.Key)
.ToList(); //returns true | false
if you want to do this based on any number then create a method and pass a param in where .Contains() extension method is being called
if you want to capture user input from Console you can do it this way as well
int [] inputDupes = {1,2,3,4,5,2};
Console.WriteLine("Enter a number to check for duplicates: ");
string input = Console.ReadLine();
int number;
Int32.TryParse(input, out number);
var dupeCount = inputDupes.Count(x => x == number);
Console.WriteLine(dupeCount);
Console.Read();
Yields 2 for the duplicate Count
static int count2(int[] input)
{
return input.Count(i => i == 2);
}
You could use a Func like this:
public Func<int[], int, int> GetNumberCount =
(numbers, numberToSearchFor) =>
numbers.Count(num => num.Equals(numberToSearchFor));
...
var count = GetNumberCount(input, 2);
Gotta' love a Func :)

Searching an array with foreach loop and getting the index of element

I have a class like so:
public class MyClass
{
public char letter { get; set; }
public double result { get; set; }
public bool test { get; set; }
}
I declare an array:
MyClass[] myArray = new MyClass[counter];
and fill it with some data.
I sort the array:
myArray = myArray.OrderBy(a => a.letter).ThenByDescending(a => a.result).ToArray();
Now let's say I have an int i = 100 variable.
How would I iterate through this array fields and get the index of the first element that:
Has specified letter in letter field.
Has test == false
result < i
I'm thinking of something like this:
foreach(MyClass t in myArray.Where(a => a.letter == 'a')
{
if(t.result < i && t.test == false) get index of that field
}
However, I'm unsure how to get the index of it. How do I do this?
Array.FindIndex should solve the problem for you:
int correctIndex = Array.FindIndex( myArray , item => item.letter == 'a' && item.result < i && !item.test );
The second parameter is functionally equivalent to how you would describe it in a .Where() clause.
Also, just like similar indexing functions, it returns -1 if the element isn't found.
You can do it using the overload of Select that provides an index, like this:
var res = myArray
.Select((val, ind) => new {val, ind}))
.Where(p => p.val.result < i && p.val.letter == 'a' && !p.val.test)
.Select(p => p.ind);
The first Select pairs up MyClass objects, as val, with their index, as ind. Then the Where method expresses the three conditions, including the one that pairs result and ind. Finally, the last Select drops the MyClass object, because it is no longer needed.
I see the guys already did a great job answering your question with better alternatives, but just in case you still want to know how to do it with for each, here is how
int counter = 5 ; // size of your array
int i = 100 ; // the limit to filter result by
int searchResult = -1; // The index of your result [If exists]
int index = 0; // index in the array
MyClass[] myArray = new MyClass[counter]; // Define you array and fill it
myArray[0] = new MyClass {letter = 'f' ,result = 12.3 , test = false } ;
myArray[1] = new MyClass {letter = 'a' ,result = 102.3 , test = true} ;
myArray[2] = new MyClass {letter = 'a' ,result = 12.3 , test = false } ;
myArray[3] = new MyClass {letter = 'b' ,result = 88 , test = true } ;
myArray[4] = new MyClass { letter = 'q', result = 234, test = false };
myArray = myArray.OrderBy(a => a.letter).ThenByDescending(a => a.result).ToArray(); // Sort the array
foreach(MyClass t in myArray.Where(a => a.letter == 'a')) // The foreach part
{
if (t.result < i && t.test == false)
{
searchResult = index;
break;
}
index++;
}
// And finally write the resulting index [If the element was found]
Please note : Of course the resulting index will be the index in the sorted array
Without foreach:
var item = myArray.FirstOrDefault(e => e.letter == 'a' && e.result < i && e.test == false);
int index = Array.IndexOf(myArray, item);

c# Array.FindAllIndexOf which FindAll IndexOf

I know c# has Array.FindAll and Array.IndexOf.
Is there a Array.FindAllIndexOf which returns int[]?
string[] myarr = new string[] {"s", "f", "s"};
int[] v = myarr.Select((b,i) => b == "s" ? i : -1).Where(i => i != -1).ToArray();
This will return 0, 2
If the value does not exist in the array then it will return a int[0].
make an extension method of it
public static class EM
{
public static int[] FindAllIndexof<T>(this IEnumerable<T> values, T val)
{
return values.Select((b,i) => object.Equals(b, val) ? i : -1).Where(i => i != -1).ToArray();
}
}
and call it like
string[] myarr = new string[] {"s", "f", "s"};
int[] v = myarr.FindAllIndexof("s");
You can write something like :
string[] someItems = { "cat", "dog", "purple elephant", "unicorn" };
var selectedItems = someItems.Select((item, index) => new{
ItemName = item,
Position = index});
or
var Items = someItems.Select((item, index) => new{
ItemName = item,
Position = index}).Where(i => i.ItemName == "purple elephant");
Read : Get the index of a given item using LINQ
Searches for an element that matches the conditions defined by the specified predicate, and returns all the zero-based index of the occurrence within the entire System.Array.
public static int[] FindAllIndex<T>(this T[] array, Predicate<T> match)
{
return array.Select((value, index) => match(value) ? index : -1)
.Where(index => index != -1).ToArray();
}
I know this is an old post, but you can try the following,
string[] cars = {"Volvo", "BMW", "Volvo", "Mazda","BMW","BMW"};
var res = Enumerable.Range(0, cars.Length).Where(i => cars[i] == "BMW").ToList();
returns {1,4,5} as a list
No, there is not. But you can write your own extension method.
public static int[] FindAllIndexOf<T>(this T[] a, Predicate<T> match)
{
T[] subArray = Array.FindAll<T>(a, match);
return (from T item in subArray select Array.IndexOf(a, item)).ToArray();
}
and then, for your array, call it.
You can loop with findIndex giving an index
string[] arr = { "abc", "asd", "def", "abc", "lmn", "wer" };
int index = -1;
do
{
index = Array.IndexOf(arr, "abc", index + 1);
System.Console.WriteLine(index);
} while (-1 != index);
I've used Nikhil Agrawal's answer to create the following related method, which may be useful.
public static List<int> FindAllIndexOf<T>(List<T> values, List<T> matches)
{
// Initialize list
List<int> index = new List<int>();
// For each value in matches get the index and add to the list with indexes
foreach (var match in matches)
{
// Find matches
index.AddRange(values.Select((b, i) => Equals(b, match) ? i : -1).Where(i => i != -1).ToList());
}
return index;
}
Which takes a list with values and a list with values that are to be matched. It returns a list of integers with the index of the matches.
You can solve this problem by creating only 2 integer variables. More power to you!
string[] seasons= { "Fall","Spring", "Summer", "Fall", "Fall", "Winter"};
int i = 0;
int IndexOfFallInArray = 0;
int[] IndexesOfFall= new int[seasons.Length];
foreach (var item in seasons)
{
if (item == "Fall")
{
IndexesOfFall[i] = IndexOfFallInArray;
i++;
}
IndexOfFallInArray++;
}
How about simply:
public static IEnumerable<int> Available()
{
for (int i = 0; i < myarr.Length; i++)
{
if (myarr[i] is null) //Your predicate here...
yield return i;
}
}
I'm aware that the question is answered already, this is just another way of doing it. note that I used ArrayList instead of int[]
// required using directives
using System;
using System.Collections;
String inputString = "The lazy fox couldn't jump, poor fox!";
ArrayList locations = new ArrayList(); // array for found indexes
string[] lineArray = inputString.Split(' '); // inputString to array of strings separated by spaces
int tempInt = 0;
foreach (string element in lineArray)
{
if (element == "fox")
{
locations.Add(tempInt); // tempInt will be the index of current found index and added to Arraylist for further processing
}
tempInt++;
}

Get Max() of alphanumeric value

I have a dictionary containg ID which are alphanumeric (e.g. a10a10 & d10a9) from which I want the biggest ID, meaning 9 < 10 < a ...
When I use the following code, d10a9 is MAX since 9 is sorted before 10
var lsd = new Dictionary<string, string>();
lsd.Add("a", "d10a10");
lsd.Add("b", "d10a9");
string max = lsd.Max(kvp => kvp.Value);
How can I get the Max value of the IDs with the Longest string combined?
I think you may try to roll your own IComparer<string>
class HumanSortComparer : IComparer<string>
{
public int Compare(string x, string y)
{
// your human sorting logic here
}
}
Usage:
var last = collection.OrderBy(x => x.Value, new HumanSortComparer()).LastOrDefault();
if (last != null)
string max = last.Value;
this works like a charm assuming IDs always start with "d10a":
int max = lsd.Max(kvp => Convert.ToInt32(kvp.Value.Substring(4)));
Console.Write(string.Format("d10a{0}", max));
One way would be to do this
string max =lsd.Where(kvp=>kvp.Value.Length==lsd.Max(k=>k.Value.Length)).Max(kvp => kvp.Value);
however I think that this method would evalute the max length for each item so you may be better to extract it to a variable first
int maxLength=lsd.Max(kvp=>kvp.Value.Length);
string max = lsd.Where(kvp=>kvp.Value.Length == maxLength).Max(kvp => kvp.Value);
If you are going to have null strings in there you may need to perform null checks too
int maxLength=lsd.Max(kvp=>(kvp.Value??String.Empty).Length);
string max = lsd.Where(kvp=>(kvp.Value??String.Empty).Length == maxLength).Max(kvp => kvp.Value);
Alternatively treat your string as Base36 number and convert to long for the max function and then convert back again to get the max string.
string max =lsd.Max(tvp=>tvp.Value.FromBase36()).ToBase36();
public static class Base36 {
public static long FromBase36(this string src) {
return src.ToLower().Select(x=>(int)x<58 ? x-48 : x-87).Aggregate(0L,(s,x)=>s*36+x);
}
public static string ToBase36(this long src) {
StringBuilder result=new StringBuilder();
while(src>0) {
var digit=(int)(src % 36);
digit=(digit<10) ? digit+48 :digit+87;
result.Insert(0,(char)digit);
src=src / 36;
}
return result.ToString();
}
}
Finally just just the Agregate extension method instead of Max as this lets you do all the comparison logic....
lsd.Agregate(string.Empty,(a,b)=> a.Length == b.Length ? (a>b ? a:b) : (a.Length>b.Length ? a:b));
This could doesn't have null checks but you easily add them in.
I think if you did this:
var max = lsd.OrderByDescending(x => x.Value)
.GroupBy(x => x.Value.Length)
.OrderByDescending(x => x.Key)
.SelectMany(x => x)
.FirstOrDefault();
It may give you what you want.
You need StringComparer.OrdinalIgnoreCase.
Without the need to use linq, the function that do that is quite simple.
Complexity is, of course, O(n).
public static KeyValuePair<string, string> FindMax(IEnumerable<KeyValuePair<string, string>> lsd)
{
var comparer = StringComparer.OrdinalIgnoreCase;
var best = default(KeyValuePair<string, string>);
bool isFirst = true;
foreach (KeyValuePair<string, string> kvp in lsd)
{
if (isFirst || comparer.Compare(kvp.Value, best.Value) > 0)
{
isFirst = false;
best = kvp;
}
}
return best;
}
Okay - I think you need to first turn each key into a series of strings and numbers - since you need the whole number to be able to determine the comparison. Then you implement an IComparer - I've tested this with your two input strings as well as with a few others and it appears to do what you want. The performance could possibly be improved - but I was brainstorming it!
Create this class:
public class ValueChain
{
public readonly IEnumerable<object> Values;
public int ValueCount = 0;
private static readonly Regex _rx =
new Regex("((?<alpha>[a-z]+)|(?<numeric>([0-9]+)))",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public ValueChain(string valueString)
{
Values = Parse(valueString);
}
private IEnumerable<object> Parse(string valueString)
{
var matches = _rx.Matches(valueString);
ValueCount = matches.Count;
foreach (var match in matches.Cast<Match>())
{
if (match.Groups["alpha"].Success)
yield return match.Groups["alpha"].Value;
else if (match.Groups["numeric"].Success)
yield return int.Parse(match.Groups["numeric"].Value);
}
}
}
Now this comparer:
public class ValueChainComparer : IComparer<ValueChain>
{
private IComparer<string> StringComparer;
public ValueChainComparer()
: this(global::System.StringComparer.OrdinalIgnoreCase)
{
}
public ValueChainComparer(IComparer<string> stringComparer)
{
StringComparer = stringComparer;
}
#region IComparer<ValueChain> Members
public int Compare(ValueChain x, ValueChain y)
{
//todo: null checks
int comparison = 0;
foreach (var pair in x.Values.Zip
(y.Values, (xVal, yVal) => new { XVal = xVal, YVal = yVal }))
{
//types match?
if (pair.XVal.GetType().Equals(pair.YVal.GetType()))
{
if (pair.XVal is string)
comparison = StringComparer.Compare(
(string)pair.XVal, (string)pair.YVal);
else if (pair.XVal is int) //unboxing here - could be changed
comparison = Comparer<int>.Default.Compare(
(int)pair.XVal, (int)pair.YVal);
if (comparison != 0)
return comparison;
}
else //according to your rules strings are always greater than numbers.
{
if (pair.XVal is string)
return 1;
else
return -1;
}
}
if (comparison == 0) //ah yes, but were they the same length?
{
//whichever one has the most values is greater
return x.ValueCount == y.ValueCount ?
0 : x.ValueCount < y.ValueCount ? -1 : 1;
}
return comparison;
}
#endregion
}
Now you can get the max using OrderByDescending on an IEnumerable<ValueChain> and FirstOrDefault:
[TestMethod]
public void TestMethod1()
{
List<ValueChain> values = new List<ValueChain>(new []
{
new ValueChain("d10a9"),
new ValueChain("d10a10")
});
ValueChain max =
values.OrderByDescending(v => v, new ValueChainComparer()).FirstOrDefault();
}
So you can use this to sort the string values in your dictionary:
var maxKvp = lsd.OrderByDescending(kvp => new ValueChain(kvp.Value),
new ValueChainComparer()).FirstOrDefault();

Categories