Get string from another string array if value matches - c#

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];
}
}

Related

OrderBy a list of contains "cm","m" and "km"

I have an array list of List<string> that contains values in the following order ["1m", "1cm", "4km","2cm"] (Centimeters, meters and kilometers)
When I want to sort this array, I get a wrong answer. I use OrderBy:
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result= data.OrderBy(x => x).ToList();
the result is:
{ "1cm", "1m", "2cm", "4km"}
But I want the answer to be this order-: { "1cm", "2cm", "1m", "4km"}
You have sorted the data alphabetically. First the first character is compared. Then the second character and...
You need to normalize the data based on cm(or m) and then sort.
List<string> data = new List<string> { "1m", "1cm", "4km","2cm" };
var result = data.OrderBy(x => lenghtCM(x));
public int lenghtCM(string lenghtStr)
{
if (lenghtStr.Contains("cm"))
{
string num = lenghtStr.Split("cm")[0];
return int.Parse(num);
}
else if (lenghtStr.Contains("km"))
{
string num = lenghtStr.Split("km")[0];
return int.Parse(num) * 100*1000;
}
else if (lenghtStr.Contains("m"))
{
string num = lenghtStr.Split('m')[0];
return int.Parse(num) * 100;
}
return 0;
}
then the result:
{ "1cm", "2cm", "1m", "4km"}
private string[] normalaizeArray(string[] inputArray)
{
for (int i= 0 ; i < inputArray.Length; i++)
{
if(inputArray[i].Contains('m'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100).ToString();
} else if(inputArray[i].Contains('km'))
{
inputArray[i] = (float.Parse(inputArray[i].Split('k')[0]) * 100*1000).ToString();
}
else
{
inputArray[i] = inputArray[i].Replace("cm", "");
}
}
inputArray = inputArray.OrderBy(x => int.Parse(x)).ToArray();
for (int i = 0; i < inputArray.Length; i++)
{
if(int.Parse(inputArray[i])>1000*100)
inputArray[i] = (float.Parse(inputArray[i])/1000).ToString() + "km";
else if(int.Parse(inputArray[i])>100)
inputArray[i] = (float.Parse(inputArray[i])/100).ToString() + "m";
else
inputArray[i] = inputArray[i] + 'cm';
}
return inputArray;
}
If you can, parse the strings first:
enum Unit { cm, m, km }
record Measurment(int Length, Unit Unit)
{
public override string ToString() => $"{Length}{Enum.GetName(typeof(Unit), Unit)}";
public double NormalizedLength => Unit switch
{
Unit.cm => Length * 0.001,
Unit.m => Length * 1.0,
Unit.km => Length * 1000.0,
_ => throw new NotImplementedException()
};
public static Measurment Parse(string source)
{
var digits = source.TakeWhile(char.IsDigit).Count();
var length = int.Parse(source.AsSpan(0, digits));
// switches with source.AsSpan(digits) in preview
var measure = source[..digits] switch
{
"cm" => Unit.cm,
"m" => Unit.m,
"km" => Unit.km,
_ => throw new NotImplementedException(),
};
return new Measurment(length, measure);
}
}
.
var result = data.Select(Measurment.Parse).OrderBy(x => x.NormalizedLength).ToList();
This lets you sort your measurments by NormalizedLength and ToString gets back the original string. Should be very fast, simple to extend with new units and you can make it fault-tolerant if you turn Parse into the TryParse pattern.
There's a NuGet package to manage parsing and manipulating SI units called UnitsNet.
If you install that package (via Add | NuGet Package, search for and select UnitsNet and install it), then you can write the following code:
(You'll need to add using UnitsNet; at the top of the code file first)
This also works with nm etc.
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
var result = data.OrderBy(Length.Parse).ToList();
Console.WriteLine(string.Join(", ", result));
This will output "1cm, 2cm, 1m, 4km"
You need custom sort using IComparable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace ConsoleApplication49
{
class Program
{
static void Main(string[] args)
{
List<string> data = new List<string> { "1m", "1cm", "4km", "2cm" };
List<string> results = data.Select(x => new SortDistance(x)).OrderBy(x => x).Select(x => x.value).ToList();
}
}
public class SortDistance : IComparable<SortDistance>
{
const string pattern = #"(?'number'\d+)(?'multiplier'.*)";
List<string> distanceOrder = new List<string>() { "cm", "m", "km" };
public string value { get; set; }
public int distance { get; set; }
public string multiplier { get; set; }
public SortDistance(string value)
{
this.value = value;
Match match = Regex.Match(value, pattern);
this.distance = int.Parse(match.Groups["number"].Value);
this.multiplier = match.Groups["multiplier"].Value;
}
public int CompareTo(SortDistance other)
{
if (this.multiplier == other.multiplier)
return this.distance.CompareTo(other.distance);
else
return distanceOrder.IndexOf(this.multiplier).CompareTo(distanceOrder.IndexOf(other.multiplier));
}
}
}
you can not sort using OrderBy.
You have to define the conversion first from all units to the smallest unit. for example m to cm, km to cm.....
so 1m euqals to 100 cm
then you have to iterate through your list and check each item's unit, get its equivalent to the smallest unit.
Create another list.
you can implement insertion sort to sort the items and add keep on inserting the item based on the comparison.

Find the shortest string in an array of string

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);

How do I get the matching values in a csv file?

So I have a .csv file with 2 columns looking a bit like this:
01,23
02,45
03,178
etc.
Now I want to read the csv file, give it a value for the first column, and get the corresponding value from the second column back. (so say I give it a value of 03, it should give me 178 back)
Here's the code I've got so far but what should go in the if statement?
public int CalculateNextLevel(int current_xp, int current_lvl)
{
var reader = new StreamReader(File.OpenRead(#"C:\Users\Lennart\Desktop\Legends of Raymere\Files\Lvl.csv"));
List<int> levels = new List<int>();
List<int> exp = new List<int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
levels.Add(Convert.ToInt32(values[0]));
exp.Add(Convert.ToInt32(values[1]));
foreach (int level in levels)
{
if (current_lvl == level)
{
}
}
}
return XP_to_nxt_lvl;
}
You can use a Dictionary instead
var expValues = new Dictionary<int, int>();
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(',');
expValues.Add(Convert.ToInt32(values[0]), Convert.ToInt32(values[1]));
}
// Retrieve value based on level
if (expValues.ContainsKey(3))
{
int level03Exp = expValues[3];
}
try this
static void Main(string[] args)
{
string key = "03";
GetValue(key);
}
private static int GetValue(string key)
{
var lines = File.ReadAllLines("test.txt");
var dictonary = lines.ToDictionary(dict =>
{
return dict.Split(',')[0];
});
int valInt = int.Parse(dictonary[key].Split(',')[1]);
return valInt;
}
You should use Linq like this :
foreach (int level in levels)
{
if (current_lvl == level)
{
XP_to_nxt_lvl = exp[levels.IndexOf(level)];
}
}
You could use LINQ and a Lookup<TKey, TValue> which allows duplicate keys but is similar to a Dictionary. If the key(level) is not present you get an empty collection of xp's:
private ILookup<int, int> LevelLookup = null;
public void LoadAllLevels()
{
LevelLookup = File.ReadLines(#"C:\Temp\Lvl.csv")
.Select(l => l.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
.Select(cols =>
{
int level = 0, xp = 0;
bool validLine = cols.Length == 2;
if(validLine)
validLine = int.TryParse(cols[0].Trim(), out level);
if(validLine)
validLine = int.TryParse(cols[1].Trim(), out xp);
return new{ level, xp, validLine };
})
.Where(x => x.validLine)
.ToLookup(x => x.level, x => x.xp);
}
public int? CalculateNextLevel(int current_xp, int current_lvl)
{
int? nextLevel = null;
var xps = LevelLookup[current_lvl];
if (xps.Any())
nextLevel = xps.First();
return nextLevel;
}
You only need to initialize it once or when the file changed via LoadAllLevels().
For example:
LoadAllLevels();
int level3 = 3;
int level4 = 4;
int? xp3 = CalculateNextLevel(100,level3);
int? xp4 = CalculateNextLevel(150,level4);
I have used nullables to differentiate between XP=0 and a level has yet no defined xp.
bool hasLev4XP = xp4.HasValue;
if(hasLev4XP)
{
int lev4XpNeeded = xp4.Value;
}
If the level is guaranteed to be unique you could also use ToDictionary to create a dictionary and use similar code as above.

Convert custom delimited string to List<myClass>?

How do I turn this:
string x = "key:value|key:value|key:value|key:value";
into this?
List<myClass> listObj;
myClass definition:
public class myClass
{
public string keyName { get; set; }
public string keyValue { get; set; }
}
There has to be a way to do it using LINQ or something :)
thanks in advance!
* NOTE *
I should add I know how to do this splitting it and looping through it, but there has to be a better way :)
This will require separate ToList() call, but I like query syntax for its declarative nature:
from s in x.Split('|')
let parts = s.Split(':')
select new myClass {
keyName = parts[0],
keyValue = parts[1]
}
Or you can use fluent syntax:
x.Split('|')
.Select(s => {
var parts = s.Split(':');
return new myClass {
keyName = parts[0],
keyValue = parts[1]
};
}).ToList()
Well, since you really wanted to avoid splitting and looping...
public List<MyClass> Parse(string base, string workingName, string workingValue,
bool processingName = true,
List<MyClass> workingList = null, int index = 0)
{
if (workingList == null)
workingList = new List<MyClass>();
if (index >= base.Length)
{
return workingList;
}
if (base[index] = '|')
{
workingList.Add(new MyClass { keyName = workingName, keyValue = workingValue });
return Parse(base, "", "", true, workingList, index + 1);
}
else if (base[index] = ':')
{
return Parse(base, workingName, "", false, workingList, index + 1);
}
else if (processingName)
{
return Parse(base, workingName + base[index], "", processingName, workingList, index + 1);
}
else
{
return Parse(base, workingName, workingValue + base[index], processingName, workingList, index + 1);
}
}
But please, for the love of whatever you hold dear, don't do anything even remotely resembling that (and yes, this is untested, hand-written code, so there are probably errors - just making a joke about avoiding things).
Do this if you prefer to use your custom class instead of Dictionary
var result = from y in x.Split('|')
let obj = y.Split(':')
select new myClass{keyName = obj[0], keyValue = obj[1]};
var list = result.ToList();

c# array help on sorting

First of all sorry for my mistakes in English its not my primary language
i have a problem , i have a array like following
string[] arr1 = new string[] {
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"India:4,USA:3,Iran:2,UK:1,Pakistan:0"
};
now i just want to know that how many times Pakistan comes with 1 ,
how many times with 2 , 3 , 4
and i need to know this about all India , USA , Iran , UK
Thanks in advance , you guys are my last hope .
This linq will convert the array into a Dictionary>, where the outer dictionary contains the countries names, and inner dictionaries will contain the ocurrence number (the number after ':') and the count for each ocurrence.
string[] arr1 = new string[]
{
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"India:4,USA:3,Iran:2,UK:1,Pakistan:0"
};
var count = arr1
.SelectMany(s => s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
.GroupBy(s => s.Split(':')[0], s => s.Split(':')[1])
.ToDictionary(g => g.Key,
g =>
{
var items = g.Distinct();
var result = new Dictionary<String, int>();
foreach (var item in items)
result[item] = g.Count(gitem => gitem == item);
return result;
});
// print the result
foreach(var country in count.Keys)
{
foreach(var ocurrence in count[country].Keys)
{
Console.WriteLine("{0} : {1} = {2}", country, ocurrence, count[country][ocurrence]);
}
}
I would use the String.Split(char[]) method and the String.SubString(int, int) method to inspect every 'country' inside your array and to get the number postfix of each country.
Try the following:
(The following code is now compiled and tested.)
Use a simple data structure to facilitate the task of holding the result of your operation.
public struct Result {
string Country { get; set; }
int Number { get; set; }
int Occurrences { get; set; }
}
// define what countries you are dealing with
string[] countries = new string[] { "Pakistan", "India", "USA", "Iran", "UK", }
Method to provide the overall result:
public static Result[] IterateOverAllCountries () {
// range of numbers forming the postfix of your country strings
int numbersToLookFor = 4;
// provide an array that stores all the local results
// numbersToLookFor + 1 to respect that numbers are starting with 0
Result[] result = new Result[countries.Length * (numbersToLookFor + 1)];
string currentCountry;
int c = 0;
// iterate over all countries
for (int i = 0; i < countries.Length; i++) {
currentCountry = countries[i];
int j = 0;
// do that for every number beginning with 0
// (according to your question)
int localResult;
while (j <= numbersToLookFor) {
localResult = FindCountryPosition(currentCountry, j);
// add another result to the array of all results
result[c] = new Result() { Country = currentCountry, Number = j, Occurrences = localResult };
j++;
c++;
}
}
return result;
}
Method to provide a local result:
// iterate over the whole array and search the
// occurrences of one particular country with one postfix number
public static int FindCountryPosition (string country, int number) {
int result = 0;
string[] subArray;
for (int i = 0; i < arr1.Length; i++) {
subArray = arr1[i].Split(',');
string current;
for (int j = 0; j < subArray.Length; j++) {
current = subArray[j];
if (
current.Equals(country + ":" + number) &&
current.Substring(current.Length - 1, 1).Equals(number + "")
)
result++;
}
}
return result;
}
The following should enable you to run the algorithm
// define what countries you are dealing with
static string[] countries = new string[] { "Pakistan", "India", "USA", "Iran", "UK", };
static string[] arr1 = new string[] {
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"Pakistan:4,India:3,USA:2,Iran:1,UK:0",
"India:4,USA:3,Iran:2,UK:1,Pakistan:0"
};
static void Main (string[] args) {
Result[] r = IterateOverAllCountries();
}
The data structure you are using is not rich enough to provide you with that information. Hence you need to parse your string and create a new data structure to be able to provide (sring[][]):
string[] arr1 = new string[] {
"Pakistan,India,USA,Iran,UK",
"Pakistan,India,USA,Iran,UK",
"India,USA,Iran,UK,Pakistan"
};
string[][] richerArray = arr1.Select(x=> x.Split('\'')).ToArray();
var countPakistanIsFirst = richerArray.Select(x=>x[0] == "Pakistan").Count();
UPDATE
You seem to have changed your question. The answer applies to the original question.

Categories

Resources