C# how write the whole array if string contains keyword - c#

I am searching if a string within an stringarray contains a keyword.
If a string gets a match i want the array(s) which the string was found in to be output on the console.
Sofar i have managed to output every string that contains a keyword within the stringarray.
I have tried to work around this by outputting the array insteed but then i get this message "System.String[]"
However, that is not my intent. I wanted the array to be displayed. I wonder, how can i do this?
//Sorry for bad english.
Here are the relevant parts from my code:
List<string[]> loggbok = new List<string[]> { };
string[] log = new string[3]; //date, title, post
DateTime date = DateTime.Now;
log[0] = "\n\tDate: " + date.ToLongDateString() + " Time: " + date.ToShortTimeString();
Console.Write("\tTitle: ");
log[1] = "\tTitle: " + Console.ReadLine();
Console.Write("\tPost: ");
log[2] = "\tPost: " + Console.ReadLine();
loggbok.Add(log);
log = new string[3];
Console.Write("\n\tSearch: ");
string keyWord;
keyWord = Console.ReadLine();
foreach (string[] item in loggbok)
{
foreach (var s in item)
{
if (s.Contains(keyWord))
{
Console.WriteLine(item);
}
}
}`enter code here`

For displaying the whole array try this:
Console.WriteLine(String.Join("\r\n", item));
You can filter the array like this:
Console.WriteLine(String.Join("\r\n", item.Where(item => item.Contains(keyWord)).ToArray());
or
string[] filtered = item.Where(s => s.Contains(keyWord)).ToArray();
Console.WriteLine(String.Join("\r\n", filtered));
If you want to filter the whole loggbok (list of string arrays) use the SelectMany extension.
string[] filtered = loggbok.SelectMany(s => s.Contains(keyWord)).ToArray();
Console.WriteLine(String.Join("\r\n", filtered));

If I understand your question correctly, you want the whole array in which the keyword has been found.
What you do wrong is you state Console.WriteLine(item) which will only print that item.
Instead, make a function which returns true if the keyword has been found in this array and false if this has not happened. Your code would look something like this:
string keyWord;
keyWord = Console.ReadLine();
foreach (string[] item in loggbok)
{
if (checkItem(item)) {
for(int i = 0; i < item.Length; i++){
Console.WriteLine(item[i]);
}
}
}
public bool checkItem(string[] item, string keyWord) {
foreach(var s in item) {
if(s.Contains(keyWord))
return true;
}
return false;
}
This might help you.

Related

C# how to write the whole array if a string within "array[1]" contains keyword

My current code is looping through a list containing saved strings in an array. Currently it looks for all strings in that array. I want to change this so that it only goes through (searching, looking) for strings within "log[1]"
Sorry, i dont know the word for "log[1]". Im new to programming. Keep reading and i think you will understand.
This is how i want to do it:
foreach (string[] item[1] in loggbok)
item[1] being log[1]. Number 1 is very important because I want to search only within log[1].
This is my current code for saving the whole array in my list:
List<string[]> loggbok = new List<string[]> { };
string[] log = new string[3]; //date, title, post
DateTime date = DateTime.Now;
log[0] = "\n\tDate: " + date.ToLongDateString() + " Kl: " + date.ToShortTimeString();
Console.WriteLine(log[0]);
Console.Write("\tTitle: ");
log[1] = "\tTitle: " + Console.ReadLine();
Console.Write("\tPost: ");
log[2] = "\tPost: " + Console.ReadLine();
loggbok.Add(log);
log = new string[3];
I save "log[1],log[2],log[3]"
The following code i want to make a search function which goes through my list and recognise all the strings within log[1] aka titles. If a string title is containing the users keyword all logs should join and the log will be printed.
As of now. I solved this by searching through all logs(1,2,3). This means that my program is searching currently for strings within (titles, date, posts). This makes it so that you can search for messages or "post" when i want the user to be restricted by only searching for titles.
So i thought maby if in my foreach loop i make "item" to "item[1]". Will that make my code to only look for "log[1]". I did not get that far though becouse writing "item[1]" is invalid syntax.
Current search function:
string key;
Console.Write("\n\tSearch: ");
key = Console.ReadLine();
//Searching through all log[] in loggbok.
//I want to change this line to item[1]
foreach (string[] item in loggbok)
{
//goes deeper and looks for all strings within log[].
foreach (string s in item)
{
//if a string is found containing key word, this block will run.
if (s.Contains(key))
{
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
}
}
}
Probably you can do it like this:
var result = loggbok.FirstOrDefault(x=> x.Any(s=> s.Contains(key));
Console.WriteLine(result?? "No record found");
You don't even need to loop, so what you need to do is retrieve the item from loggbok by the index.
// assign loggbokx of index 1, to variable item.
string[] item = loggbok[1];
// item will then have the 2nd (index=1) logbook.
// Note that index starts from 0.
// If you want to have the first one, then it should be loggbox[0]
// to make it even simpler you can write
// var item = loggbok[1];
// and the rest is the same...
//goes deeper and looks for all strings within log[].
foreach (string s in item)
{
//if a string is found containing key word, this block will run.
if (s.Contains(key))
{
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
}
}
Let's do it right!
Create a model class for your log:
class LogEntry
{
public DateTime Date { get; set; }
public string Title { get; set; }
public string Post { get; set; }
public override string ToString()
{
return "Date: " + Date.ToLongDateString() + " Kl: " + Date.ToShortTimeString()
+ "\tTitle: " + Title + "\tPost: " + Post;
}
}
Now we can comfortably use this model.
Let's populate the list with more records:
List<LogEntry> loggbok = new List<LogEntry>();
for (int i = 0; i < 5; i++)
{
LogEntry entry = new LogEntry();
entry.Date = DateTime.Now;
entry.Title = "title" + i;
entry.Post = "post" + i;
loggbok.Add(entry);
}
Let's print it:
foreach (var entry in loggbok)
Console.WriteLine(entry);
Due to the ToString method overload output looks out nice.
Let's find something:
string key = "title3";
var found = loggbok.Find(log => log.Title == key);
Console.WriteLine("Found:\n" + found);
We can use different methods of the List class, and LINQ extension methods.
If you need to save your data to a file and then read them from there, you can use json serialization.
For example, let's use the JavaScriptSerializer (don't forget to add a reference to the assembly):
JavaScriptSerializer jss = new JavaScriptSerializer();
// Save
File.WriteAllText("test.txt", jss.Serialize(loggbok));
// Load
loggbok = jss.Deserialize<List<LogEntry>>(File.ReadAllText("test.txt"));
This is the solution if anyone finds it intressting.
foreach (string[] item in loggbok)
{
foreach (string s in item)
{
//This was the magic line.
string searchTitle = item[1].ToLower();
if (searchTitle.Contains(titleKey.ToLower()))
{
Console.WriteLine("\n\tSearch hit #" + index);
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
break;
}
}
}

C# compare fields from different lines in csv

I am trying to compare the value in the 0 index of an array on one line and the 0 index on the following line. Imagine a CSV where I have a unique identifier in the first column, a corresponding value in the second column.
USER1, 1P
USER1, 3G
USER2, 1P
USER3, 1V
I would like to check the value of [0] the next line (or previous if that's easier) to compare and if they are the same (as they are in the example) concatenate it to index 1. That is, the data should read as
USER1, 1P, 3G
USER2, 1P
USER3, 1V
before it gets passed onto the next function. So far I have
private void csvParse(string path)
{
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.Delimiters = new string[] { "," };
while (!parser.EndOfData)
{
string[] parts = parser.ReadFields();
if (parts == null)
{
break;
}
contact.ContactId = parts[0];
long nextLine;
nextLine = parser.LineNumber+1;
//if line1 parts[0] == line2 parts[0] etc.
}
}
}
Does anyone have any suggestions? Thank you.
How about saving the array into a variable:
private void csvParse(string path)
{
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.Delimiters = new string[] { "," };
string[] oldParts = new string[] { string.Empty };
while (!parser.EndOfData)
{
string[] parts = parser.ReadFields();
if (parts == null || parts.Length < 1)
{
break;
}
if (oldParts[0] == parts[0])
{
// concat logic goes here
}
else
{
contact.ContactId = parts[0];
}
long nextLine;
nextLine = parser.LineNumber+1;
oldParts = parts;
//if line1 parts[0] == line2 parts[0] etc.
}
}
}
If I understand you correctly, what you are asking is essentially "how do I group the values in the second column based on the values in the first column?".
A quick and quite succinct way of doing this would be to Group By using LINQ:
var linesGroupedByUser =
from line in File.ReadAllLines(path)
let elements = line.Split(',')
let user = new {Name = elements[0], Value = elements[1]}
group user by user.Name into users
select users;
foreach (var user in linesGroupedByUser)
{
string valuesAsString = String.Join(",", user.Select(x => x.Value));
Console.WriteLine(user.Key + ", " + valuesAsString);
}
I have left out the use of your TextFieldParser class, but you can easily use that instead. This approach does, however, require that you can afford to load all of the data into memory. You don't mention whether this is viable.
The easiest way to do something like this is to convert each line to an object. You can use CsvHelper, https://www.nuget.org/packages/CsvHelper/, to do the work for you or you can iterate each line and parse to an object. It is a great tool and it knows how to properly parse CSV files into a collection of objects. Then, whether you create the collection yourself or use CsvHelper, you can use Linq to GroupBy, https://msdn.microsoft.com/en-us/library/bb534304(v=vs.100).aspx, your "key" (in this case UserId) and Aggregate, https://msdn.microsoft.com/en-us/library/bb549218(v=vs.110).aspx, the other property into a string. Then, you can use the new, grouped by, collection for your end goal (write it to file or use it for whatever you need).
You're basically finding all the unique entries so put them into a dictionary with the contact id as the key. As follows:
private void csvParse(string path)
{
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.Delimiters = new string[] { "," };
Dictionary<string, List<string>> uniqueContacts = new Dictionary<string, List<string>>();
while (!parser.EndOfData)
{
string[] parts = parser.ReadFields();
if (parts == null || parts.Count() != 2)
{
break;
}
//if contact id not present in dictionary add
if (!uniqueContacts.ContainsKey(parts[0]))
uniqueContacts.Add(parts[0],new List<string>());
//now there's definitely an existing contact in dic (the one
//we've just added or a previously added one) so add to the
//list of strings for that contact
uniqueContacts[parts[0]].Add(parts[1]);
}
//now do something with that dictionary of unique user names and
// lists of strings, for example dump them to console in the
//format you specify:
foreach (var contactId in uniqueContacts.Keys)
{
var sb = new StringBuilder();
sb.Append($"contactId, ");
foreach (var bit in uniqueContacts[contactId])
{
sb.Append(bit);
if (bit != uniqueContacts[contactId].Last())
sb.Append(", ");
}
Console.WriteLine(sb);
}
}
}

comparing two data on aspx memo with the data it contains a ";"

I enter the data in 2 aspx memo like this :
aspxmemo1 = php;visual basic;c#
aspxmemo2 = visual basic;javascript
question :
how to check if any data was similar between the 2 aspxmemo that it will show an error message.
Note:
In the above examples, the same data is: "Visual Basic".
Assuming your memo's are strings: if you split the strings on ';', you have nice collections, which are far more easy to operate on for example with Linq.
var aspxmemo1 = "php;visual basic;c#";
var aspxmemo2 = "visual basic;javascript";
var collection1 = aspxmemo1.Split(';');
var collection2 = aspxmemo2.Split(';');
if (collection1.Intersect(collection2).Any())
{
//Do Something
}
//Or iterate over the duplicate memo's (you get the point)
foreach(var item in collection1.Intersect(collection2))
{
Console.WriteLine(item + " occured in both collections!");
}
Try this way.
string aspxmemo1 = "php;visual basic;c#";
string aspxmemo2 = "visual basic;javascript";
string copies ="";
string[] group1 = aspxmemo1.Split(';');
string[] group2 = aspxmemo2.Split(';');
foreach (string x in group1)
{
if (group2.Contains<string>(x))
{
copies += x + Environment.NewLine;
}
}
MessageBox.Show(copies);

Create comma separated string from portion of strings in C# List

I have a C# List that I want to create a comma separate string. I've found other answers on SO that deal with this, but my particular case I want to only use a portion of the values in the List to create the string.
If my List contained these values:
"Foo"
"Bar"
"Car"
and I wanted to create a string
Foo, Bar and Car.
I could use this code:
string.Format("{0} and {1}.",
string.Join(", ", myList.Take(myList.Count - 1)),
myList.Last());
However, my list is actual formed of jSON values like so
{ Name = "Foo" }
{ Name = "Bar" }
{ Name = "Car" }
So the above code results in:
{ Name = "Foo" }, { Name = "Bar" } and { Name = "Car" }.
How would I construct the string such that I only use the Foo, Bar and Car values in the list?
Update
Thanks to #StevePy, this is what I ended up with:
string.Format("{0} and {1}.",
string.Join(", ", myList.Select(x => x.Name).ToList().Take(myList.Count - 1)),
myList.Select(x => x.Name).ToList().Last());
If you need to operate with strings, just grab the necessary part of each string with, for example, String.IndexOf and String.LastIndexOf methods:
List<string> myList = new List<string> {
"{ Name = \"Foo\" }",
"{ Name = \"Bar\" }",
"{ Name = \"Car\" }"
};
var temp = myList.Select(x =>
{
int index = x.IndexOf("\"") + 1;
return x.Substring(index, x.LastIndexOf("\"") - index);
})
.ToList();
string result = string.Format("{0} and {1}.",
string.Join(", ", temp.Take(myList.Count - 1)),
temp.Last());
Linq should help.
var nameList = myList.Select(x=>x.Name).ToList();
you can use JsonConvert.toString to get the value of your list item, or if you used a json serialization, you could use the JsonConvert.Deserialization
I built a method that will do this for you:
static string ConvertToMyStyle(List<string> input)
{
string result = "";
foreach(string item in input)
{
if(input.IndexOf(item) != input.ToArray().Length-1)
result += item + ", ";
else
result += "and " + item + ".";
}
return result;
}
this handles the single item case
protected string FormatWithOxfordCommas(List<string> reasons)
{
string result = "";
if (reasons.Count == 1)
result += reasons[0];
else
{
foreach (string item in reasons)
{
if (reasons.IndexOf(item) != reasons.Count - 1)
result += item + ", ";
else
result += "and " + item + ".";
}
}
return result;
}

Join collection of objects into comma-separated string

In many places in our code we have collections of objects, from which we need to create a comma-separated list. The type of collection varies: it may be a DataTable from which we need a certain column, or a List<Customer>, etc.
Now we loop through the collection and use string concatenation, for example:
string text = "";
string separator = "";
foreach (DataRow row in table.Rows)
{
text += separator + row["title"];
separator = ", ";
}
Is there a better pattern for this? Ideally I would like an approach we could reuse by just sending in a function to get the right field/property/column from each object.
string.Join(", ", Array.ConvertAll(somelist.ToArray(), i => i.ToString()))
static string ToCsv<T>(IEnumerable<T> things, Func<T, string> toStringMethod)
{
StringBuilder sb = new StringBuilder();
foreach (T thing in things)
sb.Append(toStringMethod(thing)).Append(',');
return sb.ToString(0, sb.Length - 1); //remove trailing ,
}
Use like this:
DataTable dt = ...; //datatable with some data
Console.WriteLine(ToCsv(dt.Rows, row => row["ColName"]));
or:
List<Customer> customers = ...; //assume Customer has a Name property
Console.WriteLine(ToCsv(customers, c => c.Name));
I don't have a compiler to hand but in theory it should work. And as everyone knows, in theory, practice and theory are the same. In practice, they're not.
I found string.Join and lambda Select<Func<>> helps to write minimum code.
List<string> fruits = new List<string>();
fruits.Add("Mango");
fruits.Add("Banana");
fruits.Add("Papaya");
string commaSepFruits = string.Join(",", fruits.Select(f => "'" + f + "'"));
Console.WriteLine(commaSepFruits);
List<int> ids = new List<int>();
ids.Add(1001);
ids.Add(1002);
ids.Add(1003);
string commaSepIds = string.Join(",", ids);
Console.WriteLine(commaSepIds);
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 10001, Name = "John" });
customers.Add(new Customer { Id = 10002, Name = "Robert" });
customers.Add(new Customer { Id = 10002, Name = "Ryan" });
string commaSepCustIds = string.Join(", ", customers.Select(cust => cust.Id));
string commaSepCustNames = string.Join(", ", customers.Select(cust => "'" + cust.Name + "'"));
Console.WriteLine(commaSepCustIds);
Console.WriteLine(commaSepCustNames);
Console.ReadLine();
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq
public delegate string Indexer<T>(T obj);
public static string concatenate<T>(IEnumerable<T> collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (T t in collection) sb.Append(indexer(t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// version for non-generic collections
public static string concatenate<T>(IEnumerable collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (object t in collection) sb.Append(indexer((T)t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// example 1: simple int list
string getAllInts(IEnumerable<int> listOfInts)
{
return concatenate<int>(listOfInts, Convert.ToString, ',');
}
// example 2: DataTable.Rows
string getTitle(DataRow row) { return row["title"].ToString(); }
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, getTitle, '\n');
}
// example 3: DataTable.Rows without Indexer function
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, r => r["title"].ToString(), '\n');
}
In .NET 4 you can just do string.Join(", ", table.Rows.Select(r => r["title"]))
You could write a function that transforms a IEnumerable<string> into a comma-separated string:
public string Concat(IEnumerable<string> stringList)
{
StringBuilder textBuilder = new StringBuilder();
string separator = String.Empty;
foreach(string item in stringList)
{
textBuilder.Append(separator);
textBuilder.Append(item);
separator = ", ";
}
return textBuilder.ToString();
}
You can then use LINQ to query your collection/dataset/etc to provide the stringList.
As an aside: The first modification I would make is to use the StringBuilder Class instead of just a String - it'll save resources for you.
I love Matt Howells answer in this post:
I had to make it into an extension:
public static string ToCsv<T>(this IEnumerable<T> things, Func<T, string> toStringMethod)
Usage (I am getting all the emails and turning them into a CSV string for emails):
var list = Session.Find("from User u where u.IsActive = true").Cast<User>();
return list.ToCsv(i => i.Email);
For collections you can use this method as well, for example:
string.Join(", ", contactsCollection.Select(i => i.FirstName));
You can select any property that you want to separate.
string strTest = "1,2,4,6";
string[] Nums = strTest.Split(',');
Console.Write(Nums.Aggregate<string>((first, second) => first + "," + second));
//OUTPUT:
//1,2,4,6
Here's my favorite answer adapted to the question,
and corrected Convert to ConvertAll:
string text = string.Join(", ", Array.ConvertAll(table.Rows.ToArray(), i => i["title"]));

Categories

Resources