Splitting a string value inside list<> - c#

I have a list contained within a dictionary that contains the follwing values
value a
value b
value c
value 1, value 2, value3
value d
the end result I would like is
value a
value b
value c
value 1
value 2
value 3
value d
problem is iterating through the dictionary and trying to change the collection will not work as I am trying to modify it while looping through it
string[] ar;
foreach (var kvp in dict)
{
if (kvp.Key == "STAR-016")
{
foreach (var v in kvp.Value)
{
if (v.Contains(','))
{
ar = v.Split(',');
foreach (var a in ar)
{
kvp.Value.Add(a);
}
}
}
}
}
how can i get the desired result?

Try using LINQ:
var list = new List<string>()
{
"value a",
"value b",
"value 1, value 2, value 3",
"value c"
};
/* THIS IS THE IMPORTANT PART: */
/* Replace list with kvp.Value */
list = list.SelectMany(
i => i.Split( ',' ).Select( v => v.Trim() )
).ToList();
foreach ( var item in list )
Console.WriteLine( item );
Output:
value a
value b
value 1
value 2
value 3
value c
To use this within your code:
foreach (var kvp in dict)
{
if (kvp.Key == "STAR-016")
{
var newList =
kvp.Value.SelectMany(
i => i.Split( ',' ).Select( v => v.Trim() )
);
kvp.Value.Clear();
kvp.Value.AddRange(newList);
}
}
Thanks to #Mudu for pointing out the simpler i => syntax

Assuming that this is just an example and you actually want this not only for a key STAR-016 but for all where the value (which is a List<string>) contains a comma:
Dictionary<string, List<String>> dict = new Dictionary<string, List<String>>();
dict.Add("STAR-016", new List<string>() {
"value a", "value b", "value c", "value 1, value 2, value 3", "value d"
});
foreach (var kvp in dict)
{
for (int i = kvp.Value.Count - 1; i >= 0; i--)
{
string str = kvp.Value[i];
if (str.Contains(','))
{
var parts = str.Split(',').Select(p => p.Trim());
kvp.Value.RemoveAt(i);
kvp.Value.InsertRange(i, parts);
}
}
}
Demo
I'm looping from the end to the start to avoid complications because InsertRange will add new strings which increases the Count.
I'm using RemoveAt to replace the strings with commas with new strings(one for each part splitted by comma) which are added. I'm using InsertRange instead of AddRange because you want to keep the order.
Result:
value a
value b
value c
value 1
value 2
value 3
value d

You should be able to do that with one line using SelectMany.
dict["STAR-016"] = dict["STAR-016"].SelectMany(s=>s.Split(',')).ToList();
This replaces the list for your key with one that splits the string on comma, if the string doesn't contain a comma it just returns the string. You might also consider using StringSplitOptions.RemoveEmptyEntries if you don't want empty strings resulting from consecutive commas.

You can loop trough the List using for instead of foreach and modify items
for (int i = 0; i < kvp.Value.Count; i++)
{
if (kvp.Value[i].Contains(','))
...
}

(edit: juharr's https://stackoverflow.com/a/14875503/17713 does basically the same with build-in LINQ features and in more functional style, which is often more expressive in describing the actual problem)
I'd go for an approach with yield that does not modify the original collection. Here is an example code that operates on a List<string> orig which may also be in a dictionary:
public static void Main()
{
List<string> orig = new List<string>()
{
"value a",
"value b",
"value c",
"value 1, value 2, value 3",
"value d"
};
var result = Flatten(orig).ToList();
foreach(string s in result)
{
Console.WriteLine(s);
}
}
private static IEnumerable<string> Flatten(IList<string> orig)
{
foreach(string s in orig)
{
// split anyway, if there's no colon you just get a one-element
// array containing s, see
// http://msdn.microsoft.com/en-us/library/b873y76a.aspx
foreach(string v in s.Split(','))
{
yield return v.Trim();
}
}
}
In a dictionary, you could then replace the result with the original value:
dict["STAR-016"] = Flatten(dict["STAR-016"]).ToList()
Side note: The above code finds STAR-016 directly rather than using a foreach which is slower. If you did not shorten the code and you're actually just looking up STAR-016 I'd recommend you to use this way of dictionary lookup.

Related

How do I display a list and a list of lists side by side

I found a code that displays two lists side by side but a list and a list of lists no luck
this is the code of two lists side by side
for (var i = 0; i < bncount; i++)
{
//Console.WriteLine(String.Format("{0,-10} | {1,-10}", hed.ElementAt(i),bin.ElementAt(i)));
Console.WriteLine(String.Format("{0,-10} | {1,-10}", i< hdcount ? hed[i] : string.Empty, i< bncount ? bin[i] : string.Empty));
}
but the string.empty is for lists only and not list of lists and ElementAt() also wouldn't work
I tried using linq with foreach but no success
the hed is a list of strings and bn is a list of lists of a sequence of numbers
my output are as follows
foreach(var r in bin) //0010 1110 1111
foreach(var m in hed) //red blue white
I want to have the following output
red 0010
blue 1110
white 1111
or
red blue white
0 1 1
0 1 1
1 1 1
0 0 1
Any Idea on how to do this in c# in general or in Linq? the methods I tried either resulted on reprinting one value only froma hed and all the vlaues from bin or the opposite
Not sure if I understood the question correctly, I think it would be helpful to extend the code examples including the variables definition. Anyway, if I understood correctly, this would me my approach:
var listOfString = new List<string>( )
{
"red",
"blue",
"white"
};
var listOfArrays = new List<int[]>( )
{
new int[] { 0,0,1,0 },
new int[] { 0,1,1,1 },
new int[] { 1,1,1,1 }
};
// Here you could add a condition in case you are not 100% sure your arrays are of same length.
for( var i = 0; i < listOfString.Count; i++ )
{
var stringItem = listOfString[i];
var arrayItem = listOfArrays[i];
Console.WriteLine( $"{stringItem} {string.Join( null, arrayItem )}" );
}
I would suggest using a different structure for storing your data (considering OOP principles) and the following code for printing the data out:
public class Item
{
public string Name { get; set; }
public List<int> Values { get; set; }
}
public void Print(List<Item> items)
{
foreach (var item in items)
{
Console.WriteLine($"{item.Name} {string.Join("", item.Values)}");
}
}
The first version is not so hard:
string reuslt = string.Join("\n", bin.Zip(hed).Select(x => $"{x.Item1} {x.Item2}"));
With zip, we create an enumerable of tuples, where the n-th tuple has the n-th element of bin and the n-th element of hed. You can just concatenate those two items.
The second version is a bit more complex:
result = string.Join("\t",hed) + "\n" +
string.Join("\n",Enumerable.Range(0, bin.First().Length)
.Select(x => string.Join("\t\t", bin.Select(str => str[x]))));
We create the heading line by just joing the hed strings. Then we create an enumerable of numbers which represent the indexes in the string. The enumerable will be 0, 1, 2, 3. Then we take the char with index 0 of each string of the bin list, then the char with index 1 of each string of the bin list and so on.
Online demo: https://dotnetfiddle.net/eBQ54N
You can use a dictionary:
var hed = new List<string>(){"red", "blue", "white"};
var bin = new List<string>(){ "0010", "1110", "1111" };
Dictionary<string, string> Dic = new Dictionary<string, string>();
for (int i = 0; i < hed.Count; i++)
{
Dic.Add(hed[i],bin[i]);
}
foreach (var item in Dic)
{
Console.WriteLine(item.Key+" "+item.Value);
}
Console.ReadKey();

C# looping through a list to find character counts

I'm trying to loop through a string to find the character, ASCII value, and the number of times the character occurs. So far, I have found each unique character and ASCII value using foreach statements, and finding if the value was already in the list, then don't add it, otherwise add it. However I'm struggling with the count portion. I was thinking the logic would be "if I am already in the list, don't count me again, however, increment my frequency"
I've tried a few different things, such as trying to find the index of the character it found and adding to that specific index, but i'm lost.
string String = "hello my name is lauren";
char[] String1 = String.ToCharArray();
// int [] frequency = new int[String1.Length]; //array of frequency counter
int length = 0;
List<char> letters = new List<char>();
List<int> ascii = new List<int>();
List<int> frequency = new List<int>();
foreach (int ASCII in String1)
{
bool exists = ascii.Contains(ASCII);
if (exists)
{
//add to frequency at same index
//ascii.Insert(1, ascii);
//get { ASCII[index]; }
}
else
{
ascii.Add(ASCII);
//add to frequency at new index
}
}
foreach (char letter in String1)
{
bool exists = letters.Contains(letter);
if (exists)
{
//add to frequency at same index
}
else
{
letters.Add(letter);
//add to frequency at new index
}
}
length = letters.Count;
for (int j = 0; j<length; ++j)
{
Console.WriteLine($"{letters[j].ToString(),3} {"(" + ascii[j] + ")"}\t");
}
Console.ReadLine();
}
}
}
I'm not sure if I understand your question but that what you are looking for may be Dictionary<T,T> instead of List<T>. Here are examples of solutions to problems i think you trying to solve.
Counting frequency of characters appearance
Dictionary<int, int> frequency = new Dictionary<int, int>();
foreach (int j in String)
{
if (frequency.ContainsKey(j))
{
frequency[j] += 1;
}
else
{
frequency.Add(j, 1);
}
}
Method to link characters to their ASCII
Dictionary<char, int> ASCIIofCharacters = new Dictionary<char, int>();
foreach (char i in String)
{
if (ASCIIofCharacters.ContainsKey(i))
{
}
else
{
ASCIIofCharacters.Add(i, (int)i);
}
}
A simple LINQ approach is to do this:
string String = "hello my name is lauren";
var results =
String
.GroupBy(x => x)
.Select(x => new { character = x.Key, ascii = (int)x.Key, frequency = x.Count() })
.ToArray();
That gives me:
If I understood your question, you want to map each char in the provided string to the count of times it appears in the string, right?
If that is the case, there are tons of ways to do that, and you also need to choose in which data structure you want to store the result.
Assuming you want to use linq and store the result in a Dictionary<char, int>, you could do something like this:
static IDictionary<char, int> getAsciiAndFrequencies(string str) {
return (
from c in str
group c by Convert.ToChar(c)
).ToDictionary(c => c.Key, c => c.Count());
}
And use if like this:
var f = getAsciiAndFrequencies("hello my name is lauren");
// result: { h: 1, e: 3, l: 3, o: 1, ... }
You are creating a histogram. But you should not use List.Contains as it gets ineffective as the list grows. You have to go through the list one item after another. Better use Dictionary which is based on hashing and you go directly to the item. The code may look like this
string str = "hello my name is lauren";
var dict = new Dictionary<char, int>();
foreach (char c in str)
{
dict.TryGetValue(c, out int count);
dict[c] = ++count;
}
foreach (var pair in dict.OrderBy(r => r.Key))
{
Console.WriteLine(pair.Value + "x " + pair.Key + " (" + (int)pair.Key + ")");
}
which gives
4x (32)
2x a (97)
3x e (101)
1x h (104)
1x i (105)
3x l (108)
2x m (109)
2x n (110)
1x o (111)
1x r (114)
1x s (115)
1x u (117)
1x y (121)

Creating new string lists from a Dictionary of lists ( i.e from a dictionary which has string Lists as values)

I have a dictionary string (key) and value string list. I would like to, be able to print out // contents of a list (instead of getting System.Collections.Generic.List`1[System.String] ), but also // be able to take each value ( a list of strings) and create new list
Basically I should have 3 lists in the results dictionary ( one consisting of unreal, another //containing // batter and patter and another with 4 words not containing any a's)
I have something like this
using System;
using System.Collections.Generic;
using System.Text;
static void Main(string[] args)
{
List<string> wordList = new List<string>() { "unreal", "batter", "butter", "patter",
"tested", "nested", "pested" };
char letter = 'a';
Dictionary<string, List<string>> results = new Dictionary<string, List<string>>();
// So at each key ( so the ----a- for instance) it will have it's own dictionary
foreach (string word in wordList)
{
StringBuilder builder = new StringBuilder();
// builder.Append(word);
for (int i = 0; i < word.Length; i++)
{
// builder = null;
if (word[i] == letter)
{
builder.Append(letter);
// key = String.Join(word, wordList);
// builder[i] = letter;
}
else
{
// builder[i] = '-';
builder.Append('-');
}
}
string key = builder.ToString();
// var result = string.Join(key, word);
if (results.ContainsKey(key))
{
List<string> list = results[key];
list.Add(word);
}
else
{
List<string> list = new List<string>();
list.Add(word);
results.Add(key, list);
}
// results.Add(result)
}
foreach(KeyValuePair<string,List<string>> kvp in results)
{
Console.WriteLine("{0} has {1} words", kvp.Key, kvp.Value.Count);
// Console.WriteLine(kvp.Value.Count);
}
}
A more concise way to create the dictionary would be to use GroupBy on the list to group the items by the string representation of the word where all characters are replaced with a '-' except for those that equal letter (which we create by using the Select method to select a '-' or the 'a' character, and then combine the resulting set into a string using string.Concat), and then convert that do a Dictionary<string, List<string>> using ToDictionary(), using the Group.Key for the dictionary Key, and using ToList() on the group itself to create the dictionary Value:
List<string> wordList = new List<string>()
{"unreal", "batter", "butter", "patter", "tested", "nested", "pested"};
char letter = 'a';
Dictionary<string, List<string>> results = wordList
.GroupBy(item => string.Concat(item.Select(c => c == letter ? c : '-')))
.ToDictionary(group => group.Key, group => group.ToList());
Then you can output the actual item values for each list using string.Join:
foreach (KeyValuePair<string, List<string>> kvp in results)
{
Console.Write("{0} has {1} words: ", kvp.Key, kvp.Value.Count);
Console.WriteLine(string.Join(", ", kvp.Value));
}
Output

test if string value is contained in array of strings

Hi I have a list of words which i am looking for if this word is found i want the valuye removed from my array
private static string[] WordList = { "Example No", "Name", "Client ID"};
var listToTest = new[] { "ME 0.3", "Example No.: 1243", "Name.:"};
var newList= new List<string>();
foreach (var value in listToTest)
{
if (!WordList.Any(x => x.Contains(value)))
{
newList.Add(value);
}
}
return newList.ToArray();
my test returns all the value where I only want it to return "ME 0.3"
!WordList.Any(x => x.Contains(value)) will return true if no element in WordList contains value and false if there is at least one element in WordList that contains value.
So your code will return an array of elements of listToTest, that no element in WordList contains them, but as you said you want an array of elements of listToTest that they contain no element of WordList. So in ... x.Contains(value))) replace x and value with each other:
private static string[] WordList = { "Example No", "Name", "Client ID"};
var listToTest = new[] { "ME 0.3", "Example No.: 1243", "Name.:"};
var newList= new List<string>();
foreach (var value in listToTest)
{
if (!WordList.Any(x => value.Contains(x)))
{
newList.Add(value);
}
}
return newList.ToArray();
By the way there is a neater way:
var result = listToTest.Where(x => !WordList.Any(y => x.Contains(y))).ToArray();
// Usage:
foreach (var s in result)
Console.WriteLine(s);
// Output:
// ME 0.3
Description:
Enumerable.Where Method: Filters a sequence of values based on a predicate.
Enumerable.Any Method: Determines whether any element of a sequence exists or satisfies a condition.
Enumerable.ToArray Method: Creates an array from a IEnumerable.
String.Contains(String) Method: Returns a value indicating whether a specified substring occurs within this string.
Not the most efficient, but works.
newList = listToTest.Where((x) =>
{
return WordList.Where(x2 => x.IndexOf(x2) != -1).Count() == 0;
}).ToList();

How to remove strings in list from another list?

I have 2 list which names are listA and listB.
I want to remove strings in listB which are in listA, but I want to do this in this way:
if listA contains: "bar", "bar", "bar", "foo"
and listB contains : "bar"
it removes only 1 bar and the result will be:
"bar", "bar", "foo"
the code I wrote removes all "bar":
List<string> result = listA.Except(listB).ToList();
You can try to remove it one by one:
foreach (var word in listB)
listA.Remove(word);
The Remove method will only remove one element at a time and is not throwing exception (but returning false) when the item is not found: https://msdn.microsoft.com/en-us/library/cd666k3e(v=vs.110).aspx
var listA = new List<string>() { "bar", "bar", "bar", "foo" };
var listB = new List<string>() { "bar" };
foreach (var word in listB){
listA.Remove(word);
}
Here is a more efficient way to do that:
var countB = new Dictionary<string, int>(listB.Count);
foreach (var x in listB)
{
int count;
countB.TryGetValue(x, out count);
countB[x] = count + 1;
}
listA.RemoveAll(x =>
{
int count;
if (!countB.TryGetValue(x, out count)) return false;
if (count == 1)
countB.Remove(x);
else
countB[x] = count - 1;
return true;
});
This is a faster method but it is likely to change the order of elements of first list. Steps:
Map the listA to a Dictionary<string, int> (let's call it listAMap), where key is the element of the list and value is the total number of times that value has occurred in listA;
Iterate through listB and for every element of listB, if that element is in the listAMap, reduce its count;
Get the keys of listMapA using Keys property of C# dictionaries, and iterate through all the keys. For every key which has positive value, add that key to another list a total of its count times. So if an entry is "bar" -> 2, then add "bar" twice in the new list.
Total run time of the algorithm is O(m + n), where m and n are number of elements in both the original lists. It is a better running time than other approaches mentioned here which have O(m * n) running time. Obviously this algorithm uses more space.
Supportive Code for the algorithm above:
//Step-1: Create the dictionary...
var listAMap = new Dictionary<string, int>();
foreach (var listAElement in listA)
{
listAMap.ContainsKey(listAElement) ? listAMap[listAElement]++ : listAMap.Add(listAElement, 1);
}
// Step-2: Remove the listB elements from dictionary...
foreach (var listBElement in listB)
{
if (listAMap.Contains(listBElement)) listAMap[listBElement]--;
}
//Step-3: Create the new list from pruned dictionary...
var prunedListA = new List<string>();
foreach (var key in listAMap.Keys)
{
if (listAMap[key] <= 0) continue;
for (var count = 0; count < listAMap[key]; count++)
{
prunedListA.Add(key);
}
}
//prunedListA contains the desired elements now.

Categories