Find the shortest string in an array of string - c#

I need to provide a solution to find the shortest string in an array of string. I was thinking that it should be compare the length of each string in order to return.
This is where I was stuck
static void Main(string[] args)
{
//find the shortest string in an array of string
string[] names = new string[3]{
"Tom", "and", "jerry"
};
foreach(string name in names){
Console.Write(name + ", ");
}
Console.ReadKey();
}
Can anyone help me with the compare part and explain it

this one will find the first shortest string without sorting the collection:
int minLength = names.Min(y=>y.Length); // this gets you the shortest length of all elements in names
string shortest = names.FirstOrDefault(x=>x.Length == minLength);
Explanation: it will check for that element which length is equal to the smallest length in the entire collection.
EDIT:
Can anyone help me with the compare part and explain it
To compare the length of a string use the Length property and the == operator. You can do this of course also in a loop as ChoockY did.

Use LINQ:
var shortestString = names.OrderBy(c => c.Length).FirstOrDefault();

you can do this wiht linq,
var shortestName = names.OrderBy(name => name.Length).FirstOrDefault();
or
string shortestName = names.Aggregate((a1, a2) => a1.Length <a2.Length ? a1 : a2);

You can use LINQ, as the others say. This is the easiest way to do the job, but i think, you should learn some algorithms. Finding the minimum/maximum value in an array belongs to programming basics.
Here can you read about it:
http://www.stoimen.com/blog/2012/05/21/computer-algorithms-minimum-and-maximum/
The pure c# impementation looks like:
string[] names = new string[3]{
"Tom", "and", "jerry"
};
string minValue = names[0];
foreach (string name in names)
{
if (name.Length < minValue.Length)
{
minValue = name;
}
}
Console.WriteLine(minValue);

You can use MaxBy for this. And please PLEASE do not sort the entire sequence just to find maximum. This is very wasteful, intentional wasting is the cancer that would kill performance of your software.

Nobody's provided the answer in a one-liner that runs in O(N) time. A better solution is to use .Aggregate:
var shortest = names.Aggregate((s, best) => s.Length < best.Length ? s : best)

Maybe you can do this:
string[] names = new string[3]{
"Tom", "and", "jerry"
};
var query = from n in names select n.Length;
Console.WriteLine("Shortest name: " + names[Array.IndexOf(query.ToArray(), query.ToArray().Min())]);
I hope this can be useful to someone

Write your own Linq, like this:
public static T1 Min<T1, T2>(this IEnumerable<T1> source, Func<T1, T2> selector) where T2 : IComparable<T2> {
if (source == null) {
throw new ArgumentNullException(nameof(source));
}
if (selector == null) {
throw new ArgumentNullException(nameof(selector));
}
bool firstIteration = true;
T2 minimum = default;
T1 value = default;
foreach (T1 v in source) {
T2 current = selector.Invoke(v);
if (current == null) {
throw new NullReferenceException();
}
if (firstIteration || current.CompareTo(minimum) < 0) {
minimum = current;
value = v;
firstIteration = false;
}
}
return value;
}
These variable names are bad. It is up to you to improve them.

string[] countries = { "UK", "USA", "INDIA" };
int? result = null;
string nameOfCountry = string.Empty;
foreach (var country in countries)
{
if (result == null || country.Length < result)
{
nameOfCountry = country;
result = country.Length;
}
}
Console.WriteLine(nameOfCountry);

Related

How to find the element in an string array if its content contains my search term

My string array is like this:
string[] headers = {"Row Nr", "StartDate", "StartTime", "Q_1", "Q_96"};
How can I find the element in the array, which contains my search term "date"?
I know how to find an element that it's value is equal to my search term, like this:
var match = headers.FirstOrDefault(stringToCheck => stringToCheck.Contains("StartDate"));
But I didn't find any solution that finds me an element when part of my element with IgnoreCase contains the search item, i.e., searching "date" instead of "StartDate" in this case.
This overload Contains(String value, StringComparison comparisonType) is not included in netstandard2.0.
Here is a an alternative:
string[] headers = { "Row Nr", "StartDate", "StartTime", "Q_1", "Q_96" };
var match = headers.FirstOrDefault(c => !string.IsNullOrEmpty(c) && c.IndexOf("date", StringComparison.OrdinalIgnoreCase) >= 0);
Just use another overload for Contains method:
var match = headers.FirstOrDefault(c => c.Contains("date", StringComparison.OrdinalIgnoreCase));
I found a solution to this problem as follows:
private void setDateColumnTitle ()
{
string stringToCheck = "date";
int stringToCheckIndex = -1;
string elementInArray = "Not Defined or Not Found";
if (Array.Exists<string> (headers, (Predicate<string>) delegate (string s)
{
stringToCheckIndex = s.IndexOf (stringToCheck, StringComparison.OrdinalIgnoreCase);
elementInArray = s;
return stringToCheckIndex > -1;
}))
{
dateColTitle.Text = elementInArray; //a textbox to show the output
}
}
However, I found #Lev answer simpler and shorter giving the same result as follows:
private void setDateColumnTitle ()
{
dateColTitle.Text = "Not Defined or Not Found";
var match = headers.FirstOrDefault(c => c.IndexOf("date", StringComparison.OrdinalIgnoreCase) > -1);
if (match!=null)
dateColTitle.Text = match;
}
So, it deserved to be chosen as an answer to this problem.

How do I get value from a nested list with reflection?

I have a nested list called filterTarget.
Example about content:
filterTarget = { Id: 1
InventoryDescription: {Type: "Machine", Code: "1/1", Description: "1", Category: "1", StockCode: "1", …}
InventoryRecordDetails: []
Length: "1"
Life: "04.02.2019"
Location: null
Model: "1" }
The example I want to get the value of Code of InventoryDescription.
I wrote this code for two layers but it's looking dirty and insufficient. And it's not general.
filterTarget = filterTarget
.Where(x => x.GetType()
.GetProperty(fieldval1)
.PropertyType
.GetProperty(fieldval2)
.GetValue(x.GetType()
.GetProperty(fieldval1)
.GetValue(x))
.ToString()?
.ToLower() == deger)
.ToList();
How do I get value from a nested list with reflection and linq?
If you are looking for a linq solution then let would be your friend:
filterTarget = (from f in filterTarget
let temp = f.GetType().GetProperty(fieldval1).GetValue(f)
let temp2 = temp.GetType().GetProperty(fieldval2).GetValue(temp)
where temp2.ToString().ToLower() == deger
select f).ToList();
But personnaly I find it rather unreadable. A normal loop with an appropriate if-condition would be much more maintainable:
public IEnumerable<T> FilterMyList<T>(IEnumerable<T> list, string fieldval1, string fieldval2, string deger)
{
foreach (var element in list)
{
var nesteValue1 = element.GetType().GetProperty(fieldval1).GetValue(element);
var nestedValue2 = nesteValue1.GetType().GetProperty(fieldval2).GetValue(nesteValue1);
if (nestedValue2.ToString().ToLower() == deger)
{
yield return element;
}
}
}
Here is a more general way that uses recursion to dive into your nested object. You need to provide the exact amount of propertynames and it has to match the levels of nestedness:
public IEnumerable<T> FilterMyListRecursive<T>(IEnumerable<T> list, string searchpattern, params string [] propertynames) where T: class
{
foreach (var element in list)
{
if (TryNestedRecursiveDescend(element, searchpattern, propertynames))
{
yield return element;
}
}
}
private bool TryNestedRecursiveDescend<T>(T obj, string searchpattern, params string [] propertynames) where T: class
{
var nestedValue = obj.GetType().GetProperty(propertynames.First()).GetValue(obj);
// if you are at the lowest level that you can check
if (fieldvalues.Length == 1)
{
return nestedValue.ToString().ToLower() == searchpattern;
}
else
{
// recursive call with the remaining propertynames
return TryNestedDescend(nestedValue, searchpattern, propertynames.Skip(1).ToArray());
}
}
filterTarget is my nested List.
field is ordered property string.
filterTarget = (from f in filterTarget
let temp = GetPropertyValue(f, Field)
where temp?.ToString().ToLower() == "deger"
select f).ToList();
private static object GetPropertyValue<T>(T filterTarget, string field)
{
string[] fields = field.Split('.');
object value = filterTarget;
fields?.ToList().ForEach(p =>
{
value = value.GetType().GetProperty(p).GetValue(value);
});
return value;
}

Get string from another string array if value matches

String Array 1: (In this format: <MENU>|<Not Served?>|<Alternate item served>)
Burger|True|Sandwich
Pizza|True|Hot Dog
String Array 2: (Contains Menu)
Burger
Pizza
Grill Chicken
Pasta
I need the menu is served or any alternate item served for that particular item.
Code:
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = ? // need to get alternate item
}
}
As I commented in the code, how to get the alternate item in that string array? Please help, thanks in advance.
P.S: Any help to trim if condition is also gladly welcome.
Instead of any, you may use Where to get the value matching.
#Markus is having the detailed answer, I am just using your code to find a quick fix for you.
for(int i = 0; i < strArr2.Length; i++)
{
if(strArr2.Any(_r => _r.Split('|').Any(_rS => _rS.Contains(strArr1[i]))))
{
var menu = strArr2[i];
var alternate = strArr2.Where(_rs => _rs.Split('|').Any(_rS => _rS.Contains(strArr1[i]))).First().Split('|').Last();
}
}
In order to simplify your code, it is a good idea to better separate the tasks. For instance, it will be much easier to handle the contents of string array 1 after you have converted the contents into objects, e.g.
class NotServedMenu
{
public string Menu { get; set; }
public bool NotServed { get; set; }
public string AlternateMenu { get; set; }
}
Instead of having an array of strings, you can read the strings to a list first:
private IEnumerable<NotServedMenu> NotServedMenusFromStrings(IEnumerable<string> strings)
{
return (from x in strings select ParseNotServedMenuFromString(x)).ToArray();
}
private NotServedMenu ParseNotServedMenuFromString(string str)
{
var parts = str.Split('|');
// Validate
if (parts.Length != 3)
throw new ArgumentException(string.Format("Unable to parse \"{0}\" to an object of type {1}", str, typeof(NotServedMenu).FullName));
bool notServedVal;
if (!bool.TryParse(parts[1], out notServedVal))
throw new ArgumentException(string.Format("Unable to read bool value from \"{0}\" in string \"{1}\".", parts[1], str));
// Create object
return new NotServedMenu() { Menu = parts[0],
NotServed = notServedVal,
AlternateMenu = parts[2] };
}
Once you can use the objects, the subsequent code will be much cleaner to read:
var notServedMenusStr = new[]
{
"Burger|True|Sandwich",
"Pizza|True|Hot Dog"
};
var notServedMenus = NotServedMenusFromStrings(notServedMenusStr);
var menus = new[]
{
"Burger",
"Pizza",
"Grill Chicken",
"Pasta"
};
var alternateMenus = (from m in menus join n in notServedMenus on m equals n.Menu select n);
foreach(var m in alternateMenus)
Console.WriteLine("{0}, {1}, {2}", m.Menu, m.NotServed, m.AlternateMenu);
In this sample, I've used a Linq join to find the matching items.
You could do something like that
string[] strArr1 = { "Burger|True|Sandwich", "Pizza|True|Hot Dog" };
string[] strArr2 = { "Burger", "Pizza", "Grill Chicken", "Pasta" };
foreach (string str2 in strArr2)
{
string str1 = strArr1.FirstOrDefault(str => str.Contains(str2));
if (str1 != null)
{
string[] splited = str1.Split('|');
string first = splited[0];
bool condition = Convert.ToBoolean(splited[1]);
string second = splited[2];
}
}

Get the difference between 2 strings

I'm attempting to calculate the difference between two strings
For example
string val1 = "Have a good day";
string val2 = "Have a very good day, Joe";
The result would be a list of string, with 2 items "very " and ", Joe"
So far my research into this task hasn't turned up much
Edit: The result would probably need to be 2 separate lists of strings, one that hold additions, and one that hold removals
This is the simplest version I can think of:
class Program
{
static void Main(string[] args)
{
string val1 = "Have a good day";
string val2 = "Have a very good day, Joe";
MatchCollection words1 = Regex.Matches(val1, #"\b(\w+)\b");
MatchCollection words2 = Regex.Matches(val2, #"\b(\w+)\b");
var hs1 = new HashSet<string>(words1.Cast<Match>().Select(m => m.Value));
var hs2 = new HashSet<string>(words2.Cast<Match>().Select(m => m.Value));
// Optionaly you can use a custom comparer for the words.
// var hs2 = new HashSet<string>(words2.Cast<Match>().Select(m => m.Value), new MyComparer());
// h2 contains after this operation only 'very' and 'Joe'
hs2.ExceptWith(hs1);
}
}
custom comparer:
public class MyComparer : IEqualityComparer<string>
{
public bool Equals(string one, string two)
{
return one.Equals(two, StringComparison.OrdinalIgnoreCase);
}
public int GetHashCode(string item)
{
return item.GetHashCode();
}
}
Actually i followed this steps,
(i)Obtain all words from two words irrespective of special characters
(ii)From the two lists find the difference
CODE:
string s2 = "Have a very good day, Joe";
IEnumerable<string> diff;
MatchCollection matches = Regex.Matches(s1, #"\b[\w']*\b");
IEnumerable<string> first= from m in matches.Cast<Match>()
where !string.IsNullOrEmpty(m.Value)
select TrimSuffix(m.Value);
MatchCollection matches1 = Regex.Matches(s2, #"\b[\w']*\b");
IEnumerable<string> second = from m in matches1.Cast<Match>()
where !string.IsNullOrEmpty(m.Value)
select TrimSuffix(m.Value);
if (second.Count() > first.Count())
{
diff = second.Except(first).ToList();
}
else
{
diff = first.Except(second).ToList();
}
}
static string TrimSuffix(string word)
{
int apostropheLocation = word.IndexOf('\'');
if (apostropheLocation != -1)
{
word = word.Substring(0, apostropheLocation);
}
return word;
}
OUTPUT:
very, Joe
This code:
enum Where { None, First, Second, Both } // somewhere in your source file
//...
var val1 = "Have a good calm day calm calm calm";
var val2 = "Have a very good day, Joe Joe Joe Joe";
var words1 = from m in Regex.Matches(val1, "(\\w+)|(\\S+\\s+\\S+)").Cast<Match>()
where m.Success
select m.Value.ToLower();
var words2 = from m in Regex.Matches(val2, "(\\w+)|(\\S+\\s+\\S+)").Cast<Match>()
where m.Success
select m.Value.ToLower();
var dic = new Dictionary<string, Where>();
foreach (var s in words1)
{
dic[s] = Where.First;
}
foreach (var s in words2)
{
Where b;
if (!dic.TryGetValue(s, out b)) b = Where.None;
switch (b)
{
case Where.None:
dic[s] = Where.Second;
break;
case Where.First:
dic[s] = Where.Both;
break;
}
}
foreach (var kv in dic.Where(x => x.Value != Where.Both))
{
Console.WriteLine(kv.Key);
}
Gives us 'calm', 'very', ', Joe' and 'Joe' which are differences from both strings; 'calm' from the first one and 'very', ', Joe' and 'Joe' from the next one. It also removes repeated cases.
And to get two separate lists that shows us which word came from which text:
var list1 = dic.Where(x => x.Value == Where.First).ToList();
var list2 = dic.Where(x => x.Value == Where.Second).ToList();
foreach (var kv in list1)
{
Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
foreach (var kv in list2)
{
Console.WriteLine("{0}: {1}", kv.Key, kv.Value);
}
Put the characters into two sets then compute the relative compliment of those sets.
The relative compliment will be available in any good set library.
You might want to take care to preserve the order of the characters.
you have to remove the ',' in order to get the expected result
string s1 = "Have a good day";
string s2 = "Have a very good day, Joe";
int index = s2.IndexOf(','); <----- get the index of the char to be removed
IEnumerable<string> diff;
IEnumerable<string> first = s1.Split(' ').Distinct();
IEnumerable<string> second = s2.Remove(index, 1).Split(' ').Distinct();<--- remove it
if (second.Count() > first.Count())
{
diff = second.Except(first).ToList();
}
else
{
diff = first.Except(second).ToList();
}

Sort a list in C#

How to sort a List in such a way item of list matching string comes first.
Suppose if i have
"vishal pandey"
in string then item of list matching "vishal pandey" comes first then it should show result of item containing "vishal" and item containing "pandey"
It is not possible for me bring data in that order from database
Currently I am getting list like this
var matchedProjects = (from project in unitOfWork.ProjectRepository.All()
where project.IsActive
&& project is Project
&& (
queryList.Contains(project.Name)
|| project.Name.StartsWith(query)
|| project.Name.Contains(query)
|| project.Name.EndsWith(query)
|| project.ProjectAddress.City.Name.StartsWith(query)
|| project.ProjectAddress.City.Name.Contains(query)
|| project.ProjectAddress.City.Name.EndsWith(query)
|| queryList.Contains(project.ProjectAddress.City.Name)
|| queryList.Contains(project.ProjectAddress.Address1)
)
select project as Project).Distinct().AsParallel().ToList();
-Thanks
Simplest (but not very efficient) solution is just applying lots of sorts to your items:
var keywords = "vishal pandey";
var items = new[] { "pandey", "other", "vishal", "vishal pandey" };
var query = items.OrderByDescending(i => i.Contains(keywords));
foreach (var keyword in keywords.Split())
query = query.ThenByDescending(i => i.Contains(keyword));
Output:
vishal pandey
vishal
pandey
other
But if you have many keywords, or there is lot of items, custom comparer will be much better solution.
UPDATE1: If order of partial matches will not be important, you can use this simple solution suggested by Frank:
var pattern = "vishal pandey".Replace(' ', '|');
var items = new[] { "pandey", "other", "vishal", "vishal pandey" };
var query = items.OrderByDescending(i => Regex.Matches(i, pattern).Count);
UPDATE2: Custom comparer sample
public class ItemsComparer : IComparer<string>
{
private string[] keywords;
private string pattern;
public ItemsComparer(string keywords)
{
this.keywords = keywords.Split();
this.pattern = keywords.Replace(' ', '|');
}
public int Compare(string x, string y)
{
var xMatches = Regex.Matches(x, pattern).Count;
var yMatches = Regex.Matches(y, pattern).Count;
if (xMatches != yMatches)
return yMatches.CompareTo(xMatches);
if (xMatches == keywords.Length || xMatches == 0)
return 0;
foreach (var keyword in keywords)
{
var result = y.Contains(keyword).CompareTo(x.Contains(keyword));
if (result == 0)
continue;
return result;
}
return 0;
}
}
Usage:
var items = new[] { "pandey", "other", "vishal", "vishal pandey" };
var comparer = new ItemsComparer("vishal pandey");
Array.Sort(items, comparer);
What I would do is to create a custom comparer.
Then you create a class that implements this IComparer<T> interface.
In the constructor you can pass the list of expected strings.
In the compare method you can compare two instances based on how many of the expected strings the item has.

Categories

Resources