I have a generic list of string[] arrays, and I need to build a list of strings with all possible combinations of the items in those arrays. I'm having a hard time wrapping my head around the best method.
so:
List mylist = new List; // I then populate this from the db...
The contents of mylist looks like this:
Buildings ||| Facilities ||| Fields ||| Files; Groups; Entity; ||| Controllers; FX; Steam;
The pipes " ||| " separate each string array in mylist, the semicolons are delimiters representing the items within each of those arrays. So the arrays have a minimum length of 1, and a max of N. I need to build a list of hyphen "---" separated strings with all possible combinations of the above, but staying in the order that they are in within the list. So using the above as an example, I would come up with this list of strings:
Buildings---Facilities---fields---Files---Controllers
Buildings---Facilities---fields---Groups---Controllers
Buildings---Facilities---fields---Entity---Controllers
Buildings---Facilities---fields---Files---Fx
Buildings---Facilities---fields---Groups---Fx
Buildings---Facilities---fields---Entity---Fx
Buildings---Facilities---fields---Files---Steam
Buildings---Facilities---fields---Groups---Steam
Buildings---Facilities---fields---Entity---Steam
If the 3rd array in the list had 2 items, instead of 1 ("Fields") - we'd have a list of 18 strings instead of 9 (3x3x2).
I tried using for loops, knowing which array had the largest length, and loop through each list item, but I just couldn't get it to work. Sleeping on it didn't really help.
anyone?
I would try recursion:
private void button1_Click(object sender, EventArgs e)
{
List<string[]> strs = new List<string[]>();
strs.Add(new string[] {"Buildings"});
strs.Add(new string[] {"Facilities"});
strs.Add(new string[] {"Fields"});
strs.Add(new string[] {"Files", "Groups", "Entity"});
strs.Add(new string[] {"Controllers", "FX", "Steam"});
List<string> list = AddStringsToList(strs, 0);
}
List<string> AddStringsToList(List<string[]> list, int level)
{
List<string> listOfStrings = new List<string>();
if (level == list.Count - 1)
{
foreach (string s in list[level])
{
listOfStrings.Add(s);
}
}
else if(level<list.Count-1)
{
List<string> list1 = AddStringsToList(list, level + 1);
foreach (string s in list[level])
{
foreach(string s1 in list1)
listOfStrings.Add(s + "---" + s1);
}
}
return listOfStrings;
}
Tested and it works!
I think this might do what you're looking for:
static IEnumerable<string> Combinations(IEnumerable<IEnumerable<string>> items)
{
return items.Aggregate((outs, ins) => outs.SelectMany(o => ins.Select(i => o + "---" + i)));
}
And here is an example usage
static void Main(string[] args)
{
IEnumerable<IEnumerable<string>> items = new string[][]
{
new [] { "Buildings" },
new [] { "Facilities" },
new [] { "Fields" },
new [] { "Files", "Groups", "Entity" },
new [] { "Controllers", "FX", "Steam" }
};
foreach (var c in Combinations(items))
Console.WriteLine(c);
Console.ReadLine();
}
For each set of possibilities, it takes all the strings it has so far (for example "Buildings---Facilities---fields---Files", "Buildings---Facilities---fields---Entity" etc), and then for each possibility (eg { "Controllers", "FX", "Steam"}) it appends that possibility to the the output string to get a new set of output strings. The aggregation iterates this process starting at just the possibilities for the first element as the output strings, and then repeating this "Cartesian product" successively until all the input is consumed.
Edit
For reference this is the output from the sample program:
Buildings---Facilities---Fields---Files---Controllers
Buildings---Facilities---Fields---Files---FX
Buildings---Facilities---Fields---Files---Steam
Buildings---Facilities---Fields---Groups---Controllers
Buildings---Facilities---Fields---Groups---FX
Buildings---Facilities---Fields---Groups---Steam
Buildings---Facilities---Fields---Entity---Controllers
Buildings---Facilities---Fields---Entity---FX
Buildings---Facilities---Fields---Entity---Steam
I'd like to point out that this solution is quite efficient. It doesn't use recursion, which makes it particularly efficient for cases with long chains. Also, it evaluates the result lazily which is much more memory efficient if you're dealing with many result combinations (remember these combinations can grow exponentially with the chain length).
Let's produce this instead:
Buildings---Facilities---fields---Files---Controllers
Buildings---Facilities---fields---Files---Fx
Buildings---Facilities---fields---Files---Steam
Buildings---Facilities---fields---Groups---Controllers
Buildings---Facilities---fields---Groups---Fx
Buildings---Facilities---fields---Groups---Steam
Buildings---Facilities---fields---Entity---Controllers
Buildings---Facilities---fields---Entity---Fx
Buildings---Facilities---fields---Entity---Steam
First, let's assume the data from the DB is in this format:
List<List<string>> dataFromDb;
It doesn't matter if some of the inner collections only have one value. Then something like this should do the trick:
void ConcatString(string prefix, int index, List<List<string>> collection, List<string> output)
{
if(index == collection.Count)
{
output.Add(prefix);
return;
}
var subCollection = collection[index];
foreach(var str in subCollection)
{
string newPrefix = ((prefix.Length > 0)? "---" : "") + str;
ConcatString(newPrefix, index+1, collection, output);
}
}
Called like:
var output = new List<string>();
ConcatString("", 0, dataFromDb, output);
The list you're looking for should be in output. Now, please note, I haven't run this (heck, I haven't even compiled this) so you'll need to debug it but it should get you going in the correct direction at a minimum.
Related
I generate a random string of 500 characters and want to check for words.
bliduuwfhbgphwhsyzjnlfyizbjfeeepsbpgplpbhaegyepqcjhhotovnzdtlracxrwggbcmjiglasjvmscvxwazmutqiwppzcjhijjbguxfnduuphhsoffaqwtmhmensqmyicnciaoczumjzyaaowbtwjqlpxuuqknxqvmnueknqcbvkkmildyvosczlbnlgumohosemnfkmndtiubfkminlriytmbtrzhwqmovrivxxojbpirqahatmydqgulammsnfgcvgfncqkpxhgikulsjynjrjypxwvlkvwvigvjvuydbjfizmbfbtjprxkmiqpfuyebllzezbxozkiidpplvqkqlgdlvjbfeticedwomxgawuphocisaejeonqehoipzsjgbfdatbzykkurrwwtajeajeornrhyoqadljfjyizzfluetynlrpoqojxxqmmbuaktjqghqmusjfvxkkyoewgyckpbmismwyfebaucsfueuwgio
I import a Dictionary Words txt file and check the string to see if it contains each word. If a match is found, it's added to a list.
I read using Dictionary<> is faster than Array for a words list.
When I use that method, I can see the cpu working the foreach loop in the debugger, and my loop counter goes up, about 10,000+ times in 10 seconds, but the loop continues on forever and does not return any results.
When I use Array for Dictionary, the program works, but slower at around 500 times in 10 seconds.
Not Working
Using Dictionary<>
// Random Message
public string message = Random(500);
// Dictionary Words Reference
public Dictionary<string, string> dictionary = new Dictionary<string, string>();
// Matches Found
public static List<string> matches = new List<string>();
public MainWindow()
{
InitializeComponent();
// Import Dictionary File
dictionary = File
.ReadLines(#"C:\dictionary.txt")
.Select((v, i) => new { Index = i, Value = v })
.GroupBy(p => p.Index / 2)
.ToDictionary(g => g.First().Value, g => g.Last().Value);
// If Message Contains word, add to Matches List
foreach (KeyValuePair<string, string> entry in dictionary)
{
if (message.Contains(entry.Value))
{
matches.Add(entry.Value);
}
}
}
Working
Using Array
// Random Message
public string message = Random(500);
// Dictionary Words Reference
public string[] dictionary = File.ReadAllLines(#"C:\dictionary.txt");
// Matches Found
public List<string> matches = new List<string>();
public MainWindow()
{
InitializeComponent();
// If Message Contains word, add to Matches List
foreach (var entry in dictionary)
{
if (message.Contains(entry))
{
matches.Add(entry);
}
}
}
I doubt if you want Dictionary<string, string> as a dictionary ;) HashSet<string> will be enough:
using System.Linq;
...
string source = "bliduuwfhbgphwhsyzjnlfyizbj";
HashSet<string> allWords = new HashSet<string>(File
.ReadLines(#"C:\dictionary.txt")
.Select(line => line.Trim())
.Where(line => !string.IsNullOrEmpty(line)), StringComparer.OrdinalIgnoreCase);
int shortestWord = allWords.Min(word => word.Length);
int longestWord = allWords.Max(word => word.Length);
// If you want duplicates, change HashSet<string> to List<string>
HashSet<string> wordsFound = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
for (int length = shortestWord; length <= longestWord; ++length) {
for (int position = 0; position <= source.Length - length; ++position) {
string extract = source.Substring(position, length);
if (allWords.Contains(extract))
wordsFound.Add(extract);
}
}
Test: for
https://raw.githubusercontent.com/dolph/dictionary/master/popular.txt
dictionary donwloaded as C:\dictionary.txt file
Console.WriteLine(string.Join(", ", wordsFound.OrderBy(x => x)));
we have output
id, li, lid
Using a Dictionary in this scenario doesn't make much sense. A Dictionary is, essentially, a list of variables that stores both the variable name and the variable value.
I could have the following:
int age = 21;
int money = 21343;
int distance = 10;
int year = 2017;
And convert it to a Dictionary instead, using the following:
Dictionary<string, int> numbers = new Dictionary<string, int>()
{
{ "age", 21 },
{ "money", 21343},
{ "distance", 10 },
{ "year", 2017 }
};
And then I can access a value in the dictionary using its key (the first value). So, for example, if I want to know what "age" is, I would use:
Console.Log(numbers["age"]);
This is only a single example of the power of dictionaries - there is a LOT more that they can do, and they can make your life a lot easier. In this scenario, however, they aren't going to do what you're expecting them to do. I would suggest just using the Array, or a List.
You are misusing the dictionary,
you are basically using the dictionary as a list, so it only added some overhead to the program. not helping in any way.
It would have been useful if you had something you want to query against the dictionary not the other way around.
Also, in any case, what you want is a HashSet, not a dictionary since your key in the dictionary is not the word you are querying against but an irrelevant int.
you can read more about dictionary and HashSet here:
dictionary: https://www.dotnetperls.com/dictionary
hashset: https://www.dotnetperls.com/hashset
I have the following code:
private void AddMissingValue(ref string[] someArray) {
string mightbemissing="Another Value";
if (!someArray.Contains(mightbemissing)) {
var lst=someArray.ToList();
lst.Add(mightbemissing);
someArray=lst.ToArray();
}
}
While this works (Add an item to an array if missing), I wonder if this can be done in a smarter way? I don't like converting the array twice and writing so many lines for such a simple task.
Is there a better way? Maybe using LinQ?
General idea is right - array is a fixed-sized collection and you cannot add an item to it without recreating an array.
Your method can be written in a slightly more elegant way using LINQ .Concat method without creating a List:
private void AddMissingValue(ref string[] someArray)
{
string mightbemissing = "Another Value";
if (!someArray.Contains(mightbemissing))
{
someArray = someArray.Concat(new[] { mightbemissing }).ToArray();
}
}
This implementation takes N * 2 operations which is better than your N * 3, but it is still enumerating it multiple times and is quadratic for adding N items to your array.
If you are going to perform this operation too often, then changing your code to use dynamic-size collections (f.i., List) would be a more effective way.
Even if you decide to continue using arrays, it probably (imo) will look better if you return modified array instead of using ref:
private string[] AddMissingValue(string[] someArray)
{
string mightbemissing = "Another Value";
return someArray.Contains(mightbemissing)
? someArray
: someArray.Concat(new[] { mightbemissing }).ToArray();
}
// Example usage:
string[] yourInputArray = ...;
yourInputArray = AddMissingValue(yourInputArray);
LINQ-style and the most performant
Another implementation which comes to my mind and is the best (O(N)) in terms of performance (not against dynamic-size collections, but against previous solutions) and is LINQ-styled:
public static class CollectionExtensions
{
public static IEnumerable<T> AddIfNotExists<T>(this IEnumerable<T> enumerable, T value)
{
bool itemExists = false;
foreach (var item in enumerable)
{
if (!itemExists && value.Equals(item))
itemExists = true;
yield return item;
}
if (!itemExists)
yield return value;
}
}
// Example usage:
string[] arr = ...;
arr = arr.AddIfNotExists("Another Value").ToArray();
This implementation with yield is used to prevent multiple enumeration.
If you need to add multiple items, then it can even be rewritten this way, and it seems to still be linear:
public static IEnumerable<T> AddIfNotExists<T>(this IEnumerable<T> enumerable, params T[] value)
{
HashSet<T> notExistentItems = new HashSet<T>(value);
foreach (var item in enumerable)
{
if (notExistentItems.Contains(item))
notExistentItems.Remove(item);
yield return item;
}
foreach (var notExistentItem in notExistentItems)
yield return notExistentItem;
}
// Usage example:
int[] arr = new[] { 1, 2, 3 };
arr = arr.AddIfNotExists(2, 3, 4, 5).ToArray(); // 1, 2, 3, 4, 5
You have to resize the array, see
https://msdn.microsoft.com/en-us/library/bb348051(v=vs.110).aspx
for details. Implementation:
// static: it seems that you don't want "this" in the method
private static void AddMissingValue(ref string[] someArray) {
string mightbemissing = "Another Value";
if (!someArray.Contains(mightbemissing)) {
Array.Resize(ref someArray, someArray.Length + 1);
someArray[someArray.Length - 1] = mightbemissing;
}
}
In you current implementation, you copy all the items twice which can be unwanted if the array is large
...
var lst=someArray.ToList(); // first: all data copied from array to list
lst.Add(mightbemissing);
someArray=lst.ToArray(); // second: all data copied from list to array
A better design, however, is to switch from fixed size array string[] to, say, List<string>:
List<string> someList = ...
if (!someList.Contains(mightbemissing))
someList.Add(mightbemissing); // <- just Add
if all the values should be not null and unique you can do further improvement:
HashSet<string> someHash = ...
someHash.Add(mightbemissing);
I have a string array, I need to delete all elements until a specific index or get a new array of all elements from my specific index. I'm looking for a system function without loops.
my code for example:
string []myArray = {"AAA","BBB","CCC","DDD","EEE","FFF","GGG","HHH"}
int myIndex = Array.IndexOf(myArray, "DDD");
needed output :
string []myNewArray = {"EEE","FFF","GGG","HHH"}
Just use Skip in Linq
string []myArray = {"AAA","BBB","CCC","DDD","EEE","FFF","GGG","HHH"}
int myIndex = Array.IndexOf(myArray, "DDD");
var newArray = myArray.Skip(myIndex+1);
Of course this means only that the loop is hidden from your view, but it exits nevertheless inside the Skip method.
Also, the code above, will return the whole array if the search for the string is unsuccessful.
You can use Linq's SkipWhile
string[] myArray = { "AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH" };
var myNewArray = myArray.SkipWhile(x => x != "DDD").Skip(1).ToArray();
Arrays are a pretty "dumb" object, as they mostly just have methods that effectively describe themselves. What you'll want to do is make it Queryable (one of its methods), then use LINQ to do it.
string[] myarray = GimmeAStringArray();
int x = desiredIndexValue;
return myarray.AsQueryable().Where(t => myarray.IndexOf(t) > x);
You might not need to AsQueryable() it to do that.
there is another simple way to do that using arrayList:
you can use method arraylist.RemoveRange(start index, last index)
public static void Main()
{
string[] array = new string[] {"AAA","BBB","CCC","DDD","EEE","FFF","GGG","HHH"};
List<string> list = new List<string>(array);
list.RemoveRange(0,list.IndexOf("DDD")+1);
foreach (string str in list)
{
Console.WriteLine(str);
}
}
output of program will be :
EEE
FFF
GGG
HHH
New to coding and this is probably fairly basic, but help would be appreciated.
I believe I have an issue with how I am adding note2 to majorScale[]. Once the block is finished, it returns System.String[] rather than the actual array I was expecting.
public static string[] majorScaleOfNote (string tonic)
{
string[] intervals = { "M2", "M3", "P4", "P5", "M6", "M7" };
string[] majorScale = new string[7];
majorScale [0] = tonic;
foreach (string interval in intervals) {
string note2 = intervalUpToNote (tonic, interval);
majorScale[Array.IndexOf (intervals, interval) + 1] = note2;
}
return majorScale;
}
I've narrowed down the problem to something in the string[] majorScale. When I run something without that, it produces the correct result. Something like this:
Console.WriteLine (tonic);
string[] intervals = { "M2", "M3", "P4", "P5", "M6", "M7" };
foreach (string interval in intervals) {
string note2 = intervalUpToNote (tonic, interval);
Console.WriteLine (note2);
}
Is there an problem with the way I am adding note2 to majorScale? Or is there another issue? Any help is appreciated!
The problem is most probably not in this method, but in using a result of method execution in a calling method.
I guess, you later try to Console.WriteLine the contents of its array like this:
string[] result = majorScaleOfNote("M1");
Console.WriteLine(result);
You cannot output arrays this way.
C# applies .ToString() to all objects in order to get their string representation. Arrays do not have its own ToString() method, which results in the most base class Object's .ToString() being called. It simply does the following:
public virtual String ToString()
{
return GetType().ToString();
}
That's why, it outputs its type which results to System.String[].
If you want to output all elements of an array, you could try to iterate through it yourself and output strings one by one:
string[] result = majorScaleOfNote("M1");
foreach (var note in result)
Console.WriteLine(result);
or you can use String.Join method, which joins a string using a provided separator:
string[] result = majorScaleOfNote("M1");
Console.WriteLine(string.Join(", ", result));
I need to convert a string of comma separated integers into a list of integers.
What is the best way to do this?
I can do it below but am worried about the performance - Is there a better more efficient way to do this?
public IEnumerable<int> GetListOfIds()
{
string Ids = "1,4,5,7,12"; // would be passed into method
List<int> _result = Ids.Split(',')
.ToList()
.Select(item => int.Parse(item))
.ToList();
return _result;
}
There's no need to call ToList, but otherwise your code looks fine:
public IEnumerable<int> GetListOfIds(string ids)
{
return ids.Split(',').Select(item => int.Parse(item));
}
You may also want to consider adding error handling in case the input string is invalid:
public IEnumerable<int> GetListOfIds(string ids)
{
foreach (string part in ids.Split(','))
{
int x;
if (!int.TryParse(part, out x))
{
throw new ArgumentException(
string.Format("The value {0} cannot be parsed as an integer.", part),
"ids");
}
else
{
yield return x;
}
}
}
You may at least omit first ToList() statement - so it will be
List<int> _result = Ids.Split(',').Select(item => int.Parse(item)).ToList();
You can probably loop on the split result and do a yield return int.Parse... but I would not expect big differences unless you have really many items in the original string.
Use the Stringify.Library nuget package
Example 1 (Default delimiter is implicitly taken as comma)
var ids = "1, 4, 5, 7, 12";
var splitComma = new StringConverter().ConvertTo<List<int>>(ids);
Example 2 (Specifying the delimiter explicitly)
var ids = "1; 4; 5; 7; 12";
var splitColon = new StringConverter().ConvertTo<List<int>>(ids, new ConverterOptions { Delimiter = ';' });