Match sections of a List, and Replace if both exist - c#

I've got dates from separate countries within a single List<>. I'm trying to get two records that contain the same characters before the second comma, and replace BOTH of those items with a new one.
Example:
From This:
18/04/2014,Good Friday,England and Wales
18/04/2014,Good Friday,Scotland
Into this:
18/04/2014,Good Friday,"England, Wales and Scotland"
Please note there may be multiple scenarios within the list like the above example. I've managed to get everything before the second Comma with:
splitSubstring = line.Remove(line.LastIndexOf(','));
I've tried the below, but it's clearly flawed since it won't delete both the records even if it does find a match:
foreach (var line in orderedLines)
{
if (splitSubstring == line.Remove(line.LastIndexOf(',')))
{
//Replace if previous is match here
}
splitSubstring = line.Remove(line.LastIndexOf(','));
File.AppendAllText(correctFile, line);
}

I would suggest parsing it into a structure you can work with e.g.
public class HolidayInfo
{
public DateTime Date { get; set; }
public string Name { get; set; }
public string[] Countries { get; set; }
};
And then
string[] lines = new string[]
{
"18/04/2014,Good Friday,England and Wales",
"18/04/2014,Good Friday,Scotland"
};
// splits the lines into an array of strings
IEnumerable<string[]> parsed = lines.Select(l => l.Split(','));
// copy the parsed lines into a data structure you can write code against
IEnumerable<HolidayInfo> info = parsed
.Select(l => new HolidayInfo
{
Date = DateTime.Parse(l[0]),
Name = l[1],
Countries = l[2].Split(new[] {",", " and " }, StringSplitOptions.RemoveEmptyEntries)
});
...etc. And once you have it in a helpful data structure you can begin to develop the required logic. The above code is just an example, the approach is what you should focus on.

I ended up using LINQ to pull apart the List and .Add() them into another based on an if statement. LINQ made it nice and simple.
//Using LINQ to seperate the two locations from the list.
var seperateScotland = from s in toBeInsertedList
where s.HolidayLocation == scotlandName
select s;
var seperateEngland = from e in toBeInsertedList
where e.HolidayLocation == engAndWales
select e;
Thanks for pointing me to LINQ

Related

How to remove duplicates from the list in C#

Please help me to fix this issue. My dropdown list looks something like this mentioned below.
Client
Contractor,Contractor,Contractor,Manager
Contractor,Manager
Manager
Operator
Viewer
I want to remove the duplicates and my output should be like :
Client
Contractor
Manager
Operator
Viewer
This is my code mentioned below:
Property:
public List<string> TeamRoleNames => TeamRoleUids.Select(MainRoles.GetRoleName).ToList();
Display Method:
{
result += " ; TeamRoleNames=" + this.TeamRoleNames;
}
GetRole Method:
{
string roleName;
if (RoleNameByUid.TryGetValue(roleUid, out roleName))
{
return roleName;
}
return null;
}
I have tried with Distinct Method mentioned below, But did not work like the output what I wanted!
public List<string> TeamRoleNames => TeamRoleUids.Select(MainRoles.GetRoleName).Distinct().ToList();
How can I fix this? Can anyone help?
Having elements comma separated require you to split them first to have an homogenous collection then do the distinct
// get the comma separated values out as 1 value each
// for that you can use split, remove empty and select many
// which will return as a single level list (flat)
var result = TeamRoleUids.SelectMany(o => o.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)).Distinct().ToList();
Consider converting the list to a set (hashset) since sets as a data structure doesn't allow duplicates.
More about hashsets form official documentation.
So, the solution would be similar to the following:
var hashSet = new HashSet<YourType>(yourList);
example:
var hashSet = new HashSet<string>(TeamRoleUids);
then converting it back toList() will remove duplicates.
If you have already tried Distinct and it hasn't worked, then you could do the following;
Split your string list to a List<string>
List<string> result = TeamRoleNames.Split(',').ToList();
Then when you're adding them to the dropdwon, check to see if the role is already in the dropdown. If so, move on, else add to the dropdown.
So something like
foreach(var role in this.TeamRoleNames)
{
if(!result.contains(role))
result += " ; TeamRoleNames=" + role;
}
You can use SelectMany to flatten a enumeration containing a nested enumeration. Here, we create the nested enumeration by splitting the string at the commas:
string[] input = {
"Client",
"Contractor,Contractor,Contractor,Manager",
"Contractor,Manager",
"Manager",
"Operator",
"Viewer"
};
var roles = input
.SelectMany(r => r.Split(','))
.Distinct()
.OrderBy(r => r)
.ToList();
foreach (string role in roles) {
Console.WriteLine(role);
}
prints
Client
Contractor
Manager
Operator
Viewer

How would I return the number of a sentence(s) that contains a word?

I have been trying to get better with programming (specifically c#) and am stuck on this project.
I am trying to create a windows form program where a user inputs a paragraph into a textbox and the program then returns all the unique words in the inputted paragraph as well as the count of unique words. I then need to list what sentence(s) contain these unique words.
I believe I have the first two parts down (listing unique words and their counts) however, I am struggling to think of how to complete the last part. I've listed my code below. Any advice would be great. Thanks!
namespace MrSteamyRayVaughn
{
public partial class MrSteamyRayVaughn : Form
{
public string[] paragraphWords { get; set; }
public string[] paragraphSentences { get; set; }
public MrSteamyRayVaughn()
{
InitializeComponent();
}
private void btnRun_Click(object sender, EventArgs e)
{
ParagraphSplitter();
UniqueWordCounter();
}
private void ParagraphSplitter()
{
string inputParagraph = richTextBox1.Text;
paragraphWords = inputParagraph.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
paragraphSentences = inputParagraph.Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
}
private void UniqueWordCounter()
{
var uniqueWords = paragraphWords
.OrderBy(w => w)
.GroupBy(w => w, StringComparer.InvariantCultureIgnoreCase)
.Select(grp => new
{
Word = grp.Key,
Count = grp.Count()
}
);
foreach (var u in uniqueWords)
{
richTextBox1.AppendText($"{Environment.NewLine}Word {u.Word}: Count {u.Count}");
}
}
}
}
You can use the Where clause to filter out sentences where the sentence contains Any of the items in uniqueWords:
List<string> sentencesThatContainAUniqueWord = paragraphSentences
.Where(sentence => uniqueWords.Any(sentence.Contains))
.ToList();
Or, if you want to do a case-insensitive comparison, you can use the IndexOf method, which returns the index of the first match (or -1 if no matches were found):
List<string> sentencesThatContainAUniqueWord = sentences
.Where(sentence => words.Any(word =>
sentence.IndexOf(word, StringComparison.OrdinalIgnoreCase) > -1))
.ToList();
I'm not really sure to understand what you are trying, but if I do, I will go for a different approach.
The easiest, and obvious way to count the unique words will be just to add each work to a list if they aren't already included, and then count the elements in the list.
Yeah, it's less fancy but It will do the trick and if someone comes after you will understand right away what you were trying to do.
I am responding to this on my mobile, so apologies in advance on any formatting errors.
I think that if you just say
Dictionary<string,string> uWordSentenceDictionary = new Dictionary<string,string>();
foreach (string sentence in paragraphSentences) {
string[] sentenceWords = sentence.Split(new[] { " " }, StringSplitOptions.RemoveEmptyEntries);
List<string> matches = sentenceWords.Intersect(uniqueWords).ToList();
If(matches.Any()) {
foreach(string match in matches){
uWordSentenceDictionary.Add(match, sentence);
}
}
}
uWordSentenceDictionary.ForEach( x => {
Console.Writeline($"Unique word: {x.Key}, Sentence: {x.Value}");
});
That would allow you to separate it out nicely I would think. I saw others using contains which will also solve your problem. I'm offering this as an alternative, broken down approach.

Get a single element of CSV file

I'm trying to add some csv elements to a list of Alimento, where Alimento is declared as:
namespace ContaCarboidrati
{
class Alimento
{
public virtual string Codice { get; set; }
public virtual string Descrizione { get; set; }
public virtual int Carboidrati { get; set; }
}
}
My csv looks something like this:
"C00, Pasta, 75".
Here's the method that should create the list from the csv:
private static List<Alimento> CreaListaAlimentiDaCsv()
{
List<Alimento> listaCsv = new List<Alimento>();
StreamReader sr = new StreamReader(#"C:\Users\Alex\Documents\RecordAlimenti.csv");
string abc = sr.ReadLine();
//listaCsv = abc.Split(",");
}
abc is "C00, Pasta, 75". I want to get a single element to add it to the list, or add all the 3 elements to the list, i thought that a single element is easier to made.
Sorry for my bad English
Thanks in advance
Alex
You are on the right track, but you cannot just create an Alimento of three strings, which is what you will get if you do abc.Split(","). You need to create a new Alimento object for each item (line) in the csv file and initialize each object correctly. Something like this:
var item = abc.Split(',');
listaCsv.Add(new Alimento() { Codice = item[0], Descrizione = item[1],
Carboidrati = int.Parse(item[2])};
Also, your csv seems to include spaces after the commas which you might want to get rid of. You could use string.Trim() to get rid of leading/trailing spaces. You also have to make sure the third item is actually an integer and take action if that is not the case (i.e. add some error handling).
As a side note, implementing a csv reader is not as trivial as one may think, but there are several free C# implementations out there. If you need something a bit more advanced than just reading a simple (and strictly one-line-per-item) csv, try one of these:
http://www.codeproject.com/Articles/9258/A-Fast-CSV-Reader
http://www.filehelpers.com/
You can parse file with LINQ
var listaCsv = (from line in File.ReadAllLines("RecordAlimenti.csv")
let items = line.Split(',')
select new Alimento {
Codice = items[0],
Descrizione = items[1],
Carboidrati = Int32.Parse(items[2])
}).ToList();
You can parse it pretty easy assuming your data isn't bad.
private IEnumerable<Alimento> CreaListaAlimentiDaCsv(string fileName)
{
return File.Readlines(fileName) //#"C:\Users\Alex\Documents\RecordAlimenti.csv"
.Select(line => line.Split(',').Trim())
.Select(
values =>
new Alimento
{
Codice = value[0],
Descrizione = values[0],
Carboidrati = Convert.ToInt32(values[3])
});
}
You can also use Linq on the method such as
//Takes one line without iterating the entire file
CreaListaAlimentiDaCsv(#"C:\Users\Alex\Documents\RecordAlimenti.csv").Take(1);
//Skips the first line and takes the second line reading two lines total
CreaListaAlimentiDaCsv(#"C:\Users\Alex\Documents\RecordAlimenti.csv").Skip(1).Take(1);

Get SQL LINQ Results Based off of String List

Lets start off with a list of strings that will be used to filter the results:
List<String> RadioNames = new List<String>();
RadioNames.AddRange(new String[] { "abc", "123", "cba", "321" });
I want to be able to filter a LINQ to SQL database table based on RadioNames but the catch is that I want RadioNames to be a partial match (meaning it will catch Radio123 and not just 123).
The source that I need to filter is below:
var ChannelGrants = from cg in sddc.ChannelGrants
select new
{
cg.ID,
cg.Timestamp,
cg.RadioID,
cg.Radio
};
So I need to perform something similar to below (outside of the original ChannelGrants results as this is a conditional search)
if(RadioNamesToSearch != null)
{
List<String> RadioNames = new List<String>();
// Here I split all the radio names from RadioNamesToSearch based on a command separator and then populate RadioNames with the results
ChannelGrants = from cg in ChannelGrants
where ???
select cg;
}
I need help where ??? is in the code above (or if ChannelGrants = ... is invalid all together). Repeating above, I need to filter ChannelGrants to return any matches from RadioNames but it will do partial matches (meaning it will catch Radio123 and not just 123).
All the code is contained in a method as such...
public static DataTable getBrowseChannelGrants(int Count = 300, String StartDate = null, String StartTime = null, String EndDate = null, String EndTime = null, String RadioIDs = null, String RadioNamesToSearch = null, String TalkgroupIDs = null, String TalkgroupNames = null, bool SortAsc = false)
What field in ChannelGrants are you comparing RadioNames to?
To retrieve entries that are only in your RadioNames list, you'd use the contains method like this
ChannelGrants = from cg in ChannelGrants
where RadioNames.Contains(cg.Radio)
select cg;
(If you wanted to find all rows that had one of your RadioNames in the Radio property. Replace cg.Radio with the appropriate column you are matching)
This gives you a similar outcome if you had this where clause in SQL
where cg.Radio in ("abc", "123", "cba", "321")
from this link How to do SQL Like % in Linq?
it looks like you can combo it with like matching as well, but adding slashes, by it's not something I've done personally.
in place of the ???
RadioNames.Where(rn=>cg.Radio.ToLower().Contains(rn.ToLower())).Count() > 0
That should do it...
The ToLower() calls are optional, of course.
EDIT: I just wrote this and it worked fine for me in a Console Application. The result contained one item and the WriteLine spit out "cbaKentucky". Not sure what to tell ya.
class Program
{
static void Main(string[] args)
{
List<String> RadioNames = new List<String>();
RadioNames.AddRange(new String[] { "abc", "123", "cba", "321" });
List<ChannelGrants> grants = new List<ChannelGrants>();
grants.Add(new ChannelGrants() { ID = 1, Radio = "cbaKentucky", RadioID = 1, TimeStamp = DateTime.Now });
var result = from cg in grants
where RadioNames.Where(rn=>cg.Radio.ToLower().Contains(rn.ToLower())).Count() > 0
select cg;
foreach (ChannelGrants s in result)
{
Console.WriteLine(s.Radio);
}
}
}
class ChannelGrants
{
public int ID { get; set; }
public DateTime TimeStamp { get; set; }
public int RadioID { get; set; }
public string Radio { get; set; }
}
At the moment, there doesn't seem to be a best way so I'll answer this until a new answer that doesn't repeat the other answers that don't work on this thread.

Getting values from a list using foreach

I have list that have values like"
[0] = "{ id = ES10209005, views = 501 }"
[1] = "{ id = HYT0209005, views = 5678}"
[3] = "{ id = POI0209005, views = 4568}"
I would like to pass the values(id,views) to a method using a for each loop.
method(id,views)
Something like:
foreach (string v in updatereponse)
{
method()
}
How do I isolate each value(id,views) from each row in the list then pass it to the method?
The list contains just a bunch of strings, anything based on this to fix the problem would be just a workaround (e.g. string parsing). You should really switch to a strongly typed model, e.g. define a class ViewCount:
public class ViewCount
{
public string Id {get;set;}
public int Views {get;set;}
}
You can then use a List<ViewCount> populate the list:
List<ViewCount> viewcounts = new List<ViewCount>();
viewCounts.Add(new ViewCount() { Id = "ES10209005", Views = 501 });
Since each ViewCount instance has Id and Views properties you can now do the proper thing:
foreach (var item in updatereponse)
{
method(item.Id, item.Views);
}
If you are saving this data in a file, an alternative would be to use XML instead of custom strings, then you could use Linq to XML to populate a List<ViewCount>, e.g. using a simple XML like this:
<ViewCounts>
<ViewCount id="ES10209005" views="501" />
</ViewCounts>
You can then load your list:
XElement viewXml = XElement.Load("test.xml");
List<ViewCount> viewCounts = viewXml.Descendants("ViewCount")
.Select(x => new ViewCount()
{
Id = (string)x.Attribute("id"),
Views = (int)x.Attribute("views")
}).ToList();
foreach (string v in updateresponse)
{
var values = v.Split(",");
var id = values[0].Replace("{ id = ", "").Trim();
var view = values[1].Replace("views = ", "").("}","").Trim();
method(id, value);
}
Here's another way...you may want to add error checking:
String Data = "{ id = ES10209005, views = 501 }";
String[] Segments = Data.Split(new char[] { ' ', ',' });
string ID = Segments[3];
int views = int.Parse(Segments[7]);
Assuming the structure of your String is like you showed us always, this can work for you.
// First split id and views part.
String[] firstSplit = v.Split(',');
// Get the respected value for each part.
String id = firstSplit[0].Split('=')[1].Trim();
String views = firstSplit[1].Split('=')[1].Trim().Replace("}","");
You can use String methods to retrieve the items (use Split and SubString for example) or you can use a regular expression.
E.g.
var list = UpdateResponse[0].Split("=,} ") ;
will result in a list split by all these characters (including space).
Then check the correct indices to use (use a debugger for that). Then you get something like:
var id = list[5];
var views = list[8];
(note: check the indices 5 or 8, they are just a guess).

Categories

Resources