Selecting from string from 2nd array based on 1st array results - c#

I'm looking to make selections from a second array, based on the results in my first array. The first array stores integers of the position of certain column headers reside in a csv.
var path = #"C:\Temp\file.csv";
using (TextFieldParser csvImport = new TextFieldParser(path))
{
csvImport.CommentTokens = new string[] { "#" };
csvImport.SetDelimiters(new string[] { "," });
csvImport.HasFieldsEnclosedInQuotes = true;
string[] header = csvImport.ReadFields();
foreach (string colheader in header)
{
index = index + 1; //this bit will return where my column headers are in the header line
if (colheader == "FirstColumn")
{
key = (index - 1); //stores int of my result
}
else if (colheader == "SecondColumn")
{
secondkey = (index - 1); //stores int of my result
}
}
csvImport.ReadLine(); //Behaves as carriage line return, this moves to position 2 before loop
while (!csvImport.EndOfData)
{
//Read each field, build Sql Statement
string[] fields = csvImport.ReadFields();
string MyKey = fields[1]; //Currently this is static pos 1 I want it to be the result of key
string MySecondKey = fields[74]; //Currently this is static pos 74 I want it to be the result of SecondKey
}
}
is there a simple way of assigning a variable into the [] to ad hoc pick out my array based on other variables at my disposal?
I've edited the question slightly as what I am looking to achieve is picking the field from a csv line based on the indexes from the first arra
//Read each field, build Sql Statement
string[] fields = csvImport.ReadFields();
string MyKey = fields[1]; //Currently this is static pos 1 I want it to be the result of key
string MySecondKey = fields[74]; //Currently this is static pos 74 I want it to be the result of SecondKey

Give the items in your second array an index (i here), then get all items that their index is in first array:
secondArray.Select((x,i) => new {x, i})
.Where(z => firstArray.Contains(z.i))
.Select(z => z.x);
and here is a live demo
(of course the type of second array is not important and I have used string[] just for simplicity, and it can be an array of a class, ... )

Related

How can I start a splitted string from index 1 instead of 0?

I have the following code, which reads a line from the console:
// Read text from input
var inputText = Console.ReadLine();
var inputTextSplit = inputText.Split(' ');
A possible input might be: Hello my name is John. If I try to iterate over this input now, "Hello" will be the first index (0). So inputTextSplit[0] will be equal to Hello, inputTextSplit[1] will be equal to "my", etc. What I'm trying to achieve is a way to make the index counting start at 1. So I want inputTextSplit[0] to have no value (or any filler value), and inputTextSplit[1] be "Hello", inputTextSplit[2] be "my", etc.
There are many ways to do this, here are a few
Given
var inputText = Console.ReadLine();
Simply putting an extra space on the start
var inputTextSplit = (" " + inputText).Split(' ')
Converting to List and using Insert, then converting back to an Array
var inputTextSplit = inputText.Split(' ').ToList();
inputTextSplit.Insert(0,null);
var result = inputTextSplit.ToArray();
Allocating a new array, the using Array.Copy
var inputTextSplit = inputText.Split(' ');
var result = new string[inputTextSplit.Length + 1];
Array.Copy(inputTextSplit, 0, result, 1, inputTextSplit.Length);
Resizing the array, and nulling the first element
Array.Resize(ref inputTextSplit, inputTextSplit.Length + 1);
Array.Copy(inputTextSplit, 0, inputTextSplit, 1, inputTextSplit.Length - 1);
inputTextSplit[0] = null;
using a Span.. just for giggles
var inputTextSplit = Console.ReadLine().Split(' ');
var result = new string [inputTextSplit.Length + 1];
inputTextSplit.AsSpan().CopyTo(new Span<string>(result)[1..]);
Your own iterator method
public static class Extensions
{
public static IEnumerable<string> MyOwnFunkySplit(this string source)
{
yield return null;
foreach (var item in source.Split(' '))
yield return item;
}
}
...
var inputTextSplit = Console
.ReadLine()
.MyOwnFunkySplit()
.ToArray();
You can't - split will always put the first segment into the first element of the array, which indeed has index 0.
If you really need the first segment to be at index 1 you can:
alter starting string to always have extra value
var inputTextSplit = ("skip " + inputText).Split(' ');
if you just need an enumerable where first element does not matter - concat with empty array:
var startingAtOne = (new[]{"skip"}).Concat(inputTextSplit);
copy array to a new one with an extra element at the beginning - Insert a element in String array
var startingAtOne = (new[]{"skip"}).Concat(inputTextSplit).ToArray();
Note that it is a very strange requirement and probably should be addressed in code that consumes the result.

Separate string to array

I try to get a file's content into an array in C#.
My program is similar to a shopping list.
I'm not able to store the data as a JSON file cause of some requirements.
I decided to store the data like:
3x water
2x fish
I grabbed the data using a simple StreamReader and got all the data into a string.
I tried to separate the string by \n using split and store the data into a string array.
Now I wanted to split each string again to get the quantity of the product.
I tried splitting each index using a foreach and storing them in a second array. I also tried to store the data in the same array.
I want to get something like
string[] cars = {"3", "water", "2", "fish"};
or store the data in a list.
Is my attempt a bad one?
Is there a way to do it much more easily?
Easy is a very subjective requirement.
You could write it in one statement with a variant of Split that restricts it to splitting at most once (so you can use an x in a description), and uses SelectMany to fold down all the lines into a single array of strings - as you asked.
string[] cars = File.ReadAllLines("input.txt")
.SelectMany(line => line.Split(new char[] { 'x' }, 2))
.Select(s => s.Trim())
.ToArray();
...but I wouldn't.
I think that maintainability is important. This code is understandable, useful, and obvious.
using System;
using System.Collections.Generic;
using System.IO;
class Program
{
public class Car
{
private int quantity;
private string description;
public Car(int quantity, string description)
{
this.quantity = quantity;
this.description = description;
}
public override string ToString() => $"{quantity}x {description}";
}
static void Main(string[] args)
{
// Use a list to store all the entries.
List<Car> cars = new List<Car>();
string[] lines = File.ReadAllLines("input.txt");
// Parse each line of the file.
foreach (var line in lines)
{
// Ignore completely blank lines.
if (string.IsNullOrWhiteSpace(line))
continue;
// Find the delimiter 'x'.
int pos = line.IndexOf('x');
// Handle case where no delimiter is present.
if (pos < 0)
throw new FormatException("The line is in an invalid format because it does not contain an 'x'.");
// Split the string into two parts at the delimiter position.
string firstPart = line.Substring(0, pos); // everything before the delimiter
string lastPart = line.Substring(pos + 1); // everything after the delimiter
// Interpret the first part as an integer.
if (!int.TryParse(firstPart, out int quantity))
throw new FormatException("The quantity is not a number.");
// Disallow zero or less for quantities.
if (quantity < 1)
throw new InvalidDataException("The quantity is not a positive number.");
// Trim whitespace from the description.
string description = lastPart.Trim();
// Require a non-empty description.
if (string.IsNullOrWhiteSpace(description))
throw new InvalidDataException("The description is missing.");
cars.Add(new Car(quantity, description));
}
}
}
If we suppose that each item has it's own line inside the file, this solution goes through each line and separates the quantity and the name of the item, after that these are stored in an array.
private static string[] GetData()
{
using (StreamReader sr = new StreamReader("source.in"))
{
int numberOfItems = File.ReadAllLines("source.in").Length;
string[] Data = new string[numberOfItems * 2];
int DataIndex = -1;
string line;
while ((line = sr.ReadLine()) != null)
{
//Separate the things
int index = line.IndexOf('x');
string num = line.Substring(0, index++);
Data[++DataIndex] = num;
string itm = line.Substring(++index);
Data[++DataIndex] = itm;
}
return Data;
}
}

string.PadRight() doesn't seem to work in my code

I have a Powershell output to re-format, because formatting gets lost in my StandardOutput.ReadToEnd().There are several blanks to be removed in a line and I want to get the output formatted readable.
Current output in my messageBox looks like
Microsoft.MicrosoftJigsaw All
Microsoft.MicrosoftMahjong All
What I want is
Microsoft.MicrosoftJigsaw All
Microsoft.MicrosoftMahjong All
What am I doing wrong?
My C# knowledge still is basic level only
I found this question here, but maybe I don't understand the answer correctly. The solution doesn't work for me.
Padding a string using PadRight method
This is my current code:
string first = "";
string last = "";
int idx = line.LastIndexOf(" ");
if (idx != -1)
{
first = line.Substring(0, idx).Replace(" ","").PadRight(10, '~');
last = line.Substring(idx + 1);
}
MessageBox.Show(first + last);
String.PadLeft() first parameter defines the length of the padded string, not padding symbol count.
Firstly, you can iterate through all you string, split and save.
Secondly, you should get the longest string length.
Finally, you can format strings to needed format.
var strings = new []
{
"Microsoft.MicrosoftJigsaw All",
"Microsoft.MicrosoftMahjong All"
};
var keyValuePairs = new List<KeyValuePair<string, string>>();
foreach(var item in strings)
{
var parts = item.Split(new [] {" "}, StringSplitOptions.RemoveEmptyEntries);
keyValuePairs.Add(new KeyValuePair<string, string>(parts[0], parts[1]));
}
var longestStringCharCount = keyValuePairs.Select(kv => kv.Key).Max(k => k.Length);
var minSpaceCount = 5; // min space count between parts of the string
var formattedStrings = keyValuePairs.Select(kv => string.Concat(kv.Key.PadRight(longestStringCharCount + minSpaceCount, ' '), kv.Value));
foreach(var item in formattedStrings)
{
Console.WriteLine(item);
}
Result:
Microsoft.MicrosoftJigsaw All
Microsoft.MicrosoftMahjong All
The PadRight(10 is not enough, it is the size of the complete string.
I would probably go for something like:
string[] lines = new[]
{
"Microsoft.MicrosoftJigsaw All",
"Microsoft.MicrosoftMahjong All"
};
// iterate all (example) lines
foreach (var line in lines)
{
// split the string on spaces and remove empty ones
// (so multiple spaces are ignored)
// ofcourse, you must check if the splitted array has atleast 2 elements.
string[] splitted = line.Split(new Char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
// reformat the string, with padding the first string to a total of 40 chars.
var formatted = splitted[0].PadRight(40, ' ') + splitted[1];
// write to anything as output.
Trace.WriteLine(formatted);
}
Will show:
Microsoft.MicrosoftJigsaw All
Microsoft.MicrosoftMahjong All
So you need to determine the maximum length of the first string.
Assuming the length of second part of your string is 10 but you can change it. Try below piece of code:
Function:
private string PrepareStringAfterPadding(string line, int totalLength)
{
int secondPartLength = 10;
int lastIndexOfSpace = line.LastIndexOf(" ");
string firstPart = line.Substring(0, lastIndexOfSpace + 1).Trim().PadRight(totalLength - secondPartLength);
string secondPart = line.Substring(lastIndexOfSpace + 1).Trim().PadLeft(secondPartLength);
return firstPart + secondPart;
}
Calling:
string line1String = PrepareStringAfterPadding("Microsoft.MicrosoftJigsaw All", 40);
string line2String = PrepareStringAfterPadding("Microsoft.MicrosoftMahjong All", 40);
Result:
Microsoft.MicrosoftJigsaw All
Microsoft.MicrosoftMahjong All
Note:
Code is given for demo purpose please customize the totalLength and secondPartLength and calling of the function as per your requirement.

How to retrieve each value from string and add them to the list

This is my code so far in C#
string myString = "value:12345, data:34298, end, value2:678910, data2:48957, end, value3:56546, data3:83576, end";
var tuple = new List<Tuple<int, int>>();
foreach (var value in myString)
{
int startPos = myString.IndexOf("value:") + "value:".Length;
int length = myString.LastIndexOf(", data") - startPos;
string valuesX = myString.Substring(startPos, length);
int values = int.Parse(valuesX);
int startPos2 = myString.IndexOf("data:") + "data:".Length;
int length2 = myString.LastIndexOf(", end") - startPos2;
string dataX = myString.Substring(startPos2, length2);
int data = int.Parse(dataX);
tuple.Add(Tuple.Create(values, data));
}
I want to retrieve the value number and data number from string and put them into a list using integer data type
this picture demonstrates what I want to achieve:
The code simply does not work and I would appreciate any help, thank you
1st you can't just loop through a string like that, you will need to split it.
Under the following assumptions:
The structure Value:#, Data:#, end never changes
The number for value and data are always integers
then the following will work.
string myString = "value:12345, data:34298, end, value2:678910, data2:48957, end, value3:56546, data3:83576, end";
List<Tuple<int, int>> tuple = new List<Tuple<int, int>>();
// first split the string on commas so we have a collection to loop through
string[] splitString = myString.Split(',');
// Assuming the Value:#, Data:#, end structure, we can loop by 3 each time
for (int i = 0; i < splitString.Length; i = i+3)
{
/* Assuming the above structure, we can be sure that
* i = Value
* i+1 = Data
* i+2 = end - which we can ignore
* all will be integers by assumption so no need to check, just parse
* Always in string:# pattern so we split on : and take the second item
*/
int value = Int32.Parse(splitString[i].Split(':')[1]);
int data = Int32.Parse(splitString[i + 1].Split(':')[1]);
tuple.Add(Tuple.Create(value, data));
}
You can use a regular expression. Here is my code below.
string myString = "value:12345, data:34298, end, value2:678910, data2:48957, end, value3:56546, data3:83576, end";
var matches = Regex.Matches(myString, #"value[0-9]*\:([0-9]+) *, * data[0-9]*\:([0-9]+)");
var pairs = new List<(int key, int value)>();
foreach (Match match in matches)
{
var a = int.Parse(match.Groups[1].ToString());
var b = int.Parse(match.Groups[2].ToString());
pairs.Add((a,b));
}
Using Linq, you can process the string into substrings and then into values in Lists:
var tuples = myString.Split(new[] { ", end" }, StringSplitOptions.None) ' split into data strings
.Select(s => s.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries) ' split into data string pairs
.Select(s2 => Int32.Parse(s2.Split(':')[1])).ToList()) ' extract number and make int32 pairs
.Where(s => s.Count > 0) ' exclude empty data strings
.Select(s => (s[0], s[1])).ToList(); ' create tuples and put in a List

Insert blank strings into array based on index

Let's say I have an array for example
string[] A = {"1","2","3","4","5"}
I want the array to be of size 10, and want to insert blank strings after a certain index.
For example, I could make it size 10 and insert strings after index 3 which would result in
A = {"1","2","3","4","","","","","","5"}
Basically the elements after the given index will be pushed to the end and blank strings will take the empty space in between.
This is what I tried but it only adds one string and doesnt exactly set a size for the array
var foos = new List<string>(A);
foos.Insert(33, "");
foos[32] = "";
A = foos.ToArray();
You can use InsertRange
var l = new List<string>{"1","2","3","4","5"};
l.InsertRange(3, new string[10 - l.Count]);
foreach(var i in l)
Console.WriteLine(i);
Note: The above doesn't populate with empty strings but null values, but you can easily modify the new string[] being used to be populated with your desired default.
For example; see How to populate/instantiate a C# array with a single value?
Here is LINQ based approach:
public string[] InsertToArray(string[] array, int index, int newArrayCapacity)
{
var firstPart = array.Take(index + 1);
var secondPart = array.Skip(index + 1);
var newPart = Enumerable.Repeat(String.Empty, newArrayCapacity - array.Length);
return firstPart.Concat(newPart).Concat(secondPart).ToArray();
}
Here is the usage of the method:
string[] A = {"1","2","3","4","5"};
// Insert 5 elements (so that A.Length will be 10) in A after 3rd element
var result = InsertToArray(A, 3, 10);
Added: see Sayse's answer, really the way to go
Since arrays are fixed sized collections, you can't resize an array.What you need to do is to split your array elements, you need to get the elements before and after the specified index,you can do that by using Skip and Take methods, then you need to generate a sequence of empty strings and put them together:
string[] A = {"1","2","3","4","5"};
int index = 3;
var result = A.Take(index + 1)
.Concat(Enumerable.Repeat("", 10 - A.Length))
.Concat(A.Skip(index+1))
.ToArray();
If you don't mind using a list instead of an array, you can do it this way:
int index = 3;
int numberOfBlanksToInsert = 5;
List<string> strings = new List<string>();
for (int i = 0; i < numberOfBlanksToInsert; i++)
{
strings.Insert(index, "");
}
You can also output this to an array when you're done:
string[] A = strings.ToArray();
static string[] InsertRange(string[] initialValues, int startIndex, int count, string toInsert)
{
string[] result = new string[initialValues.Length + count];
for (int i = 0; i < initialValues.Length + count; i++)
result[i] = i < startIndex ? initialValues[i] : i >= startIndex + count ? initialValues[i - count] : toInsert;
return result;
}
Usage : InsertRange(A, 4, 5, "hello");
Output : "1, 2, 3, 4, hello, hello, hello, hello, hello, 5"

Categories

Resources