Sort subdomains based on domains - c#

I have list of domains and sub-domains like
abc.com
def.com
ijk.com
pages.abc.com
help.abc.com
contactus.def.com
help.def.com
My Requirement is to sort this list by domains, such that the final output is
abc.com
pages.abc.com
help.abc.com
def.com
contactus.def.com
ijk.com
How can i achieve this in C#? Im new to C# programming. Can anybody help?

Think you made an error sorting the example but here's a solution:
class DomainComparer : IComparer<string>
{
public int Compare(string x, string y)
{
if(x==y) return 0;
string[] _x = x.Split('.');
string[] _y = y.Split('.');
return Compare(_x, _y, 0);
}
private int Compare(string[] x, string[] y, int depth)
{
if (x.Length - depth - 1 >= 0 && y.Length - depth -1 < 0)
{
return +1;
}
if (y.Length - depth - 1 >= 0 && x.Length - depth -1 < 0)
{
return -1;
}
if (x[x.Length-depth-1].CompareTo(y[y.Length - depth-1]) == 0)
{
return Compare(x, y, depth + 1);
}
else
{
return x[x.Length-depth-1].CompareTo(y[y.Length - depth-1]);
}
}
}
Then you can call it with:
string[] domains = new[] { "abc.com", "def.com", "ijk.com", "pages.abc.com", "help.abc.com", "contactus.def.com", "help.def.com" };
Array.Sort(domains, new DomainComparer());
foreach (var item in domains)
{
Console.WriteLine(item);
}
Output:
abc.com
help.abc.com
pages.abc.com
def.com
contactus.def.com
help.def.com
ijk.com
Or if you dont have an array but a
List<string>
or an
IEnumerable<string>
you can do it with Linq:
IEnumerable<string> sorted = domains.OrderBy(x => x, new DomainComparer());

If you only need to sort by second level domain & TLD then you can do something like this.
var uriList = new string[] {"abc.com", "def.com", "ijk.com", "pages.abc.com",
"help.abc.com", "contactus.def.com", "help.def.com"};
var query = from uri in uriList.Select(item =>
new { Uri = item, UriParts = item.Split('.') })
orderby uri.UriParts[uri.UriParts.Length-2] + uri.UriParts[uri.UriParts.Length-1],
string.Join(".", uri.UriParts) select uri.Uri;
Console.WriteLine(string.Join(" ,", query));
The output will be slightly different from what you are expecting, like this.
abc.com, help.abc.com, pages.abc.com, contactus.def.com, def.com, help.def.com, ijk.com

it could be done like this:
List<string> list = new List<string>();
list.Add("abc.com");
list.Add("def.com");
list.Add("ijk.com");
list.Add("pages.abc.com");
list.Add("help.abc.com");
list.Add("contactus.def.com");
list.Add("help.def.com");
List<string> listRoot = new List<string>();
List<string> listSub = new List<string>();
foreach (string item in list)
{
string[] split = item.Split(new char[] {'.'}, StringSplitOptions.RemoveEmptyEntries);
if (split.Length == 2)
{
listRoot.Add(item);
}
else
{
listSub.Add(item);
}
}
listRoot.Sort();
Dictionary<string, List<string>> dictionary = new Dictionary<string, List<string>>();
foreach (string root in listRoot)
{
List<string> listSubIntern = new List<string>();
foreach (string item in listSub)
{
if (item.EndsWith(root, StringComparison.InvariantCultureIgnoreCase))
{
listSubIntern.Add(item);
}
}
listSubIntern.Sort();
dictionary.Add(root, listSubIntern);
}
foreach (KeyValuePair<string, List<string>> keyValuePair in dictionary)
{
Console.WriteLine(keyValuePair.Key);
foreach (string s in keyValuePair.Value)
{
Console.WriteLine("\t{0}", s);
}
}
and the output:
abc.com
help.abc.com
pages.abc.com
def.com
contactus.def.com
help.def.com
ijk.com

Related

C# find anagram from a string array of candidates

My task is to implement a method that could return a correct sublist of anagrams.
Now so far I am having problems with figuring out how can I collect the anagrams in candidates that match word and return it.
This is my code for now:
public class Anagram
{
public string word;
public Anagram(string sourceWord)
{
if (sourceWord is null)
{
throw new ArgumentNullException(nameof(sourceWord));
}
if (sourceWord.Length == 0)
{
throw new ArgumentException(null);
}
this.word = sourceWord;
}
public string[] FindAnagrams(string[] candidates)
{
if (candidates is null)
{
throw new ArgumentNullException(nameof(candidates));
}
char[] char1 = this.word.ToLower().ToCharArray();
Array.Sort(char1);
string newWord1 = new string(char1);
string newWord2;
string[] result = new string[candidates.Length];
for (int i = 0; i < candidates.Length; i++)
{
char[] char2 = candidates[i].ToLower().ToCharArray();
Array.Sort(char2);
newWord2 = char2.ToString();
if (newWord1 == newWord2)
{
result[i] = candidates[i];
}
}
return result;
}
}
Should I do a second for loop in the if statement or something else.
And by the way how I am doing with my class constructor, It's the first time I am trying to use it and at the end I don't think I called the sourceWord variable correctly..
This is one of my test scenarios I would need to pass later on:
[TestCase("master", new[] { "stream", "pigeon", "maters" }, ExpectedResult = new[] { "stream", "maters" })]
[TestCase("listen", new[] { "enlists", "google", "inlets", "banana" }, ExpectedResult = new[] { "inlets" })]
[TestCase("allergy", new[] { "gallery", "ballerina", "regally", "clergy", "largely", "leading" }, ExpectedResult = new[] { "gallery", "regally", "largely" })]
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates)
{
var sut = new Anagram(word);
return sut.FindAnagrams(candidates);
}
Unfortunatly, can't use LINQ on this task.
If two words are anagrams, they have the same numbers of same letters:
art ~ rat ~ tar
we can sort letters within each word and group words by such keys:
...
aaabnn: banana
aemrst: maters, stream
...
Code:
using System.Linq;
...
// Given list of (candidates) word return anagrams
public static IEnumerable<string[]> FindAnagrams(IEnumerable<string> candidates) {
if (null == candidates)
throw new ArgumentNullException(nameof(candidates));
return candidates
.GroupBy(word => string.Concat(word.OrderBy(c => c)))
.Where(group => group.Count() > 1)
.Select(group => group.OrderBy(word => word).ToArray());
}
Demo:
string[] demo = new string[] {
"stream", "pigeon", "maters",
"enlists", "google", "inlets", "banana",
"gallery", "ballerina", "regally", "clergy", "largely", "leading",
"art", "tar", "rat"
};
string report = string.Join(Environment.NewLine, FindAnagrams(demo)
.Select(group => string.Join(", ", group)));
Console.Write(report);
Outcome:
maters, stream
gallery, largely, regally
art, rat, tar
Edit: Same idea for FindAnagrams_Detects_Anagrams:
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates) {
if (word == null || candidates == null)
return new string[0];
string[] wordArr =
string key = string.Concat(word.OrderBy(c => c));
return candidates
.Where(w => w != null)
.Where(w => key == string.Concat(w.OrderBy(c => c)))
.ToArray();
}
You can get rid of Linq if you want:
All anagrams:
public static IEnumerable<string[]> FindAnagrams(IEnumerable<string> candidates) {
if (null == candidates)
throw new ArgumentNullException(nameof(candidates));
Dictionary<string, List<string>> groups =
new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
foreach (var word in candidates) {
char[] keyArray = word.ToCharArray();
Array.Sort(keyArray);
string key = string.Concat(keyArray);
if (groups.TryGetValue(key, out var list))
list.Add(word);
else
groups.Add(key, new List<string>() { word});
}
foreach (var pair in groups) {
if (pair.Value.Count > 1) {
string[] result = new string[pair.Value.Count];
for (int i = 0; i < pair.Value.Count; ++i)
result[i] = pair.Value[i];
yield return result;
}
}
}
Detect anagrams:
public string[] FindAnagrams_Detects_Anagrams(string word, string[] candidates) {
if (word == null || candidates == null)
return new string[0];
char[] keyArray = word.ToCharArray();
Array.Sort(keyArray);
string key = string.Concat(keyArray);
List<string> list = new List<string>();
foreach (string w in candidates) {
char[] wArray = w.ToCharArray();
Array.Sort(wArray);
string wKey = string.Concat(wArray);
if (string.Equals(wKey, key, StringComparison.OrdinalIgnoreCase))
list.Add(w);
}
string[] result = new string[list.Count];
for (int i = 0; i < list.Count; ++i)
result[i] = list[i];
return result;
}

How Sort A List With C# For A Specific Part Of Every Item In List

please see the list below :
string[] Separator = new string[] { "__" };
string[] lines_acc = File.ReadAllLines(accfilePath);
List<string> list_lines_acc = new List<string>(lines_acc);
List<string> list_lines_silver_count = new List<string>();
FileStream fs_ = null;
if (!File.Exists(silver_countfilePath))
{
using (fs_ = File.Create(silver_countfilePath))
{
foreach (string line_acc in list_lines_acc)
{
string[] line_acc_ar = line_acc.Split(Separator, StringSplitOptions.None);
string line_acc_new = line_acc_ar[0] + "__" + line_acc_ar[1] + "__" + line_acc_ar[3] + "__" + line_acc_ar[4] + "__" + "0";
list_lines_silver_count.Add(line_acc_new);
}
File.WriteAllLines(silver_countfilePath, list_lines_silver_count);
}
}
else
{
string[] lines_silver_count = File.ReadAllLines(silver_countfilePath);
list_lines_silver_count = new List<string>(lines_silver_count);
}
i want to sort list_lines_silver_count by line_acc_ar[4] part!
that part is a string like -> 325423 -> mean i can convert it to an integer.
how can i do that job?
One way is to implement the comparer and provide it as an argument to the Sort function:
public class SilverCountLineComparer : IComparer<string>
{
public int Compare(string x, string y)
{
string xPart = x.Split(new char[] {'_'}, StringSplitOptions.RemoveEmptyEntries)[3];
string yPart = y.Split(new char[] {'_'}, StringSplitOptions.RemoveEmptyEntries)[3];
int xNum = Int32.Parse(xPart);
int yNum = Int32.Parse(yPart);
return xNum.CompareTo(yNum);
}
}
And to sort call it like this:
list_lines_silver_count.Sort(new SilverCountLineComparer());
Change the foreach:
foreach (var line_acc_ar in list_lines_acc
.Select(s => s.Split(Separator, StringSplitOptions.None)
.OrderBy(a => a[4])) {
}
Further refactorings could make the code more elegant, but I think this piece of LINQ should do the job.
You can try this
sorted using a Comparison generic delegate representing the CompareStringByInteger method
public static int CompareStringByInteger(string x, string y)
{
if (x == null)
{
if (y == null)
return 0;
else
return -1;
}
else
{
try{
return Convert.ToInt32(x).CompareTo(Convert.ToInt32(y));
}catch{
return x.CompareTo(y);
}
}
}
and apply it in sort method.
list_lines_silver_count.Sort(CompareStringByInteger);
You can use Linq:
int number = 0;
string[] lines_silver_count = File
.ReadLines(silver_countfilePath)
.Select(l => new {
Line = l,
Parts = l.Split(Separator, StringSplitOptions.None)
})
.Where(x => x.Parts.Length > 4
&& int.TryParse(x.Parts[4], out number))
.OrderBy(x => number)
.Select(x => x.Line)
.ToArray();
public class MyCompare : IComparer<string>
{
public int Compare(string x, string y)
{
//get line_acc_ar[4] part from strings x and y
string[] Separator = new string[] { "__" };
string partX = x.Split(Separator, StringSplitOptions.None)[3];
string partY = y.Split(Separator, StringSplitOptions.None)[3];
int intPartX = int.Parse(partX );
int inrPartY = int.Parse(partY );
return intPartX.CompareTo(inrPartY)
}
}
list_lines_silver_count.OrderBy(a => a, new MyCompare());

Combine similar character in string in C#

I have a list of lists that contain integers (this list can be any length and can contain any number of integers:
{{1,2}, {3,4}, {2,4}, {9,10}, {9,12,13,14}}
What I want to do next is combine the lists where any integer matches any integer from any other list, in this case:
result = {{1,2,3,4}, {9,10,12,13,14}}
I have tried many different approaches but am stuck for an elegant solution.
If you just mean "combine when there's an intersection", then maybe something like below, with output:
{1,2,3,4}
{9,10,12}
noting that it also passes the test in your edit, with output:
{1,2,3,4}
{9,10,12,13,14}
Code:
static class Program {
static void Main()
{
var sets = new SetCombiner<int> {
{1,2},{3,4},{2,4},{9,10},{9,12}
};
sets.Combine();
foreach (var set in sets)
{
// edited for unity: original implementation
// Console.WriteLine("{" +
// string.Join(",", set.OrderBy(x => x)) + "}");
StringBuilder sb = new StringBuilder();
foreach(int i in set.OrderBy(x => x)) {
if(sb.Length != 0) sb.Append(',');
sb.Append(i);
}
Console.WriteLine("{" + sb + "}");
}
}
}
class SetCombiner<T> : List<HashSet<T>>
{
public void Add(params T[] values)
{
Add(new HashSet<T>(values));
}
public void Combine()
{
int priorCount;
do
{
priorCount = this.Count;
for (int i = Count - 1; i >= 0; i--)
{
if (i >= Count) continue; // watch we haven't removed
int formed = i;
for (int j = formed - 1; j >= 0; j--)
{
if (this[formed].Any(this[j].Contains))
{ // an intersection exists; merge and remove
this[j].UnionWith(this[formed]);
this.RemoveAt(formed);
formed = j;
}
}
}
} while (priorCount != this.Count); // making progress
}
}
Build custom comparer:
public class CusComparer : IComparer<int[]>
{
public int Compare(int[] x, int[] y)
{
x = x.OrderBy(i => i).ToArray();
y = y.OrderBy(i => i).ToArray();
for (int i = 0; i < Math.Min(x.Length, y.Length); i++ )
{
if (x[i] < y[i]) return -1;
if (x[i] > y[i]) return 1;
}
if (x.Length < y.Length) return -1;
if (x.Length > y.Length) return 1;
return 0;
}
}
Then, order by custom comparer first:
List<int[]> input = new List<int[]>()
{
new[] { 3, 4 }, new[] { 1, 2 }, new[] { 2, 4 },
new[] { 9, 10 }, new[] { 9, 12 }
};
var orderedInput = input.OrderBy(x => x, new CusComparer()).ToList();
Use Intersect.Any() to check:
List<int[]> output = new List<int[]>();
int[] temp = orderedInput[0];
foreach (var arr in orderedInput.Skip(1))
{
if (temp.Intersect(arr).Any())
temp = temp.Union(arr).ToArray();
else
{
output.Add(temp);
temp = arr;
}
}
output.Add(temp);
Here's a simple, flexible solution using LINQ's Aggregate:
void Main()
{
var ints = new []{new []{1,2},new []{3,4},new []{2,4},new []{9,10},new []{9,12}};
var grouped = ints.Aggregate(new List<HashSet<int>>(), Step);
foreach(var bucket in grouped)
Console.WriteLine(String.Join(",", bucket.OrderBy(b => b)));
}
static List<HashSet<T>> Step<T>(List<HashSet<T>> all, IEnumerable<T> current)
{
var bucket = new HashSet<T>();
foreach (var c in current)
bucket.Add(c);
foreach (var i in all.Where(b => b.Overlaps(bucket)).ToArray())
{
bucket.UnionWith(i);
all.Remove(i);
}
all.Add(bucket);
return all;
}
We maintain a list of resulting sets (1). For each source set, remove resulting sets that intersect it (2), and add a new resulting set (3) that is the union of the removed sets and the source set (4):
class Program {
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> sets,
IEqualityComparer<T> eq
) {
var result_sets = new LinkedList<HashSet<T>>(); // 1
foreach (var set in sets) {
var result_set = new HashSet<T>(eq); // 3
foreach (var element in set) {
result_set.Add(element); // 4
var node = result_sets.First;
while (node != null) {
var next = node.Next;
if (node.Value.Contains(element)) { // 2
result_set.UnionWith(node.Value); // 4
result_sets.Remove(node); // 2
}
node = next;
}
}
result_sets.AddLast(result_set); // 3
}
return result_sets;
}
static IEnumerable<IEnumerable<T>> CombineSets<T>(
IEnumerable<IEnumerable<T>> src
) {
return CombineSets(src, EqualityComparer<T>.Default);
}
static void Main(string[] args) {
var sets = new[] {
new[] { 1, 2 },
new[] { 3, 4 },
new[] { 2, 4 },
new[] { 9, 10 },
new[] { 9, 12, 13, 14 }
};
foreach (var result in CombineSets(sets))
Console.WriteLine(
"{{{0}}}",
string.Join(",", result.OrderBy(x => x))
);
}
}
This prints:
{1,2,3,4}
{9,10,12,13,14}
Ok i LINQed this up! Hope this is what you wanted... crazy one ;)
void Main()
{
var matches = new List<List<ComparissonItem>> { /*Your Items*/ };
var overall =
from match in matches
let matchesOne =
(from searchItem in matches
where searchItem.Any(item => match.Any(val => val.Matches(item) && !val.Equals(item)))
select searchItem)
where matchesOne.Any()
select
matchesOne.Union(new List<List<ComparissonItem>> { match })
.SelectMany(item => item);
var result = overall.Select(item => item.ToHashSet());
}
static class Extensions
{
public static HashSet<T> ToHashSet<T>(this IEnumerable<T> enumerable)
{
return new HashSet<T>(enumerable);
}
}
class ComparissonItem
{
public int Value { get; set; }
public bool Matches(ComparissonItem item)
{
/* Your matching logic*/
}
public override bool Equals(object obj)
{
var other = obj as ComparissonItem;
return other == null ? false : this.Value == other.Value;
}
public override int GetHashCode()
{
return this.Value.GetHashCode();
}
}

Combining arrays of strings together

I'm looking to combine the contents of two string arrays, into a new list that has the contents of both joined together.
string[] days = { "Mon", "Tue", "Wed" };
string[] months = { "Jan", "Feb", "Mar" };
// I want the output to be a list with the contents
// "Mon Jan", "Mon Feb", "Mon Mar", "Tue Jan", "Tue Feb" etc...
How can I do it ? For when it's only two arrays, the following works and is easy enough:
List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
var combinedWords = new List<string>();
foreach (var wordOne in wordsOne)
{
foreach (string wordTwo in wordsTwo)
{
combinedWords.Add(wordOne + " " + wordTwo);
}
}
return combinedWords;
}
But I'd like to be able to pass varying numbers of arrays in (i.e. to have a method with the signature below) and have it still work.
List<string> CombineWords(params string[][] arraysOfWords)
{
// what needs to go here ?
}
Or some other solution would be great. If it's possible to do this simply with Linq, even better!
What you want to do is actually a cartesian product of all the arrays of words, then join the words with spaces. Eric Lippert has a simple implementation of a Linq cartesian product here. You can use it to implement CombineWords:
List<string> CombineWords(params string[][] arraysOfWords)
{
return CartesianProduct(arraysOfWords)
.Select(x => string.Join(" ", x))
.ToList();
}
To cross join on any amount of arrays of strings:
// Define other methods and classes here
List<string> CombineWords(params string[][] arraysOfWords)
{
if (arraysOfWords.Length == 0)
return new List<string>();
IEnumerable<string> result = arraysOfWords[0];
foreach( string[] words in arraysOfWords.Skip(1) )
{
var tempWords = words;
result = from r in result
from w in tempWords
select string.Concat(r, " ", w);
}
return result.ToList();
}
Code below works for any number of arrays (and uses linq to some degree):
List<string> CombineWords(params string[][] wordsToCombine)
{
if (wordsToCombine.Length == 0)
return new List<string>();
IEnumerable<string> combinedWords = wordsToCombine[0].ToList();
for (int i = 1; i < wordsToCombine.Length; ++i)
{
var temp = i;
combinedWords = (from x in combinedWords from y in wordsToCombine[temp]
select x + " " + y);
}
return combinedWords.ToList();
}
public static List<string> CombineWords(params string[][] arraysOfWords)
{
var strings = new List<string>();
if (arraysOfWords.Length == 0)
{
return strings;
}
Action<string, int> combineWordsInternal = null;
combineWordsInternal = (baseString, index) =>
{
foreach (var str in arraysOfWords[index])
{
string str2 = baseString + " " + str;
if (index + 1 < arraysOfWords.Length)
{
combineWordsInternal(str2, index + 1);
}
else
{
strings.Add(str2);
}
}
};
combineWordsInternal(string.Empty, 0);
return strings;
}
Second try... I'm not able to do it in LINQ... A little too much complex to linquize correctly :-)
I'm using a local anonymous function (and showing that it's quite complex to recurse on anonymous functions, because you have to declare them separately)
This is a non-recursive solution which buffers strings as it progresses, to reduce the number of concatenations. Therefore it should also be usable for more arrays.
It also preserves your desired order - the items in the first array will always be at the beginning of the resulting string.
var s1 = new string[] { "A", "B", "C" };
var s2 = new string[] { "1", "2", "3", "4" };
var s3 = new string[] { "-", "!", "?" };
var res = Combine(s1, s2, s3);
And the function in question:
private List<string> Combine(params string[][] arrays)
{
if (arrays.Length == 1)
{
// The trivial case - exit.
return new List<string>(arrays[0]);
}
IEnumerable<string> last = arrays[arrays.Length - 1];
// Build from the last array, progress forward
for (int i = arrays.Length - 2; i >= 0; i--)
{
var buffer = new List<string>();
var current = arrays[i];
foreach (var head in current)
{
foreach (var tail in last)
{
// Concatenate with the desired space.
buffer.Add(head + " " + tail);
}
}
last = buffer;
}
return (List<string>)last;
}
Could you try this method ?
static List<string> CombineWords(string[] wordsOne, string[] wordsTwo)
{
var combinedWords = new List<string>();
for(int x = 0; (x <= wordsOne.Length - 1); ++x)
{
for(int y = 0; (x <= wordsTwo.Length - 1); ++y)
{
combinedWords.Add(string.Format("{0} {1}", wordsOne[x], wordsTwo[y]));
}
}
return combinedWords;
}
Kris

How to convert a String[] to an IDictionary<String, String>?

How to convert a String[] to an IDictionary<String, String>?
The values at the indices 0,2,4,... shall be keys, and consequently values at the indices 1,3,5,... shall be values.
Example:
new[] { "^BI", "connectORCL", "^CR", "connectCR" }
=>
new Dictionary<String, String> {{"^BI", "connectORCL"}, {"^CR", "connectCR"}};
I'd recommend a good old for loop for clarity. But if you insist on a LINQ query, this should work:
var dictionary = Enumerable.Range(0, array.Length/2)
.ToDictionary(i => array[2*i], i => array[2*i+1])
Dictionary<string,string> ArrayToDict(string[] arr)
{
if(arr.Length%2!=0)
throw new ArgumentException("Array doesn't contain an even number of entries");
Dictionary<string,string> dict=new Dictionary<string,string>();
for(int i=0;i<arr.Length/2;i++)
{
string key=arr[2*i];
string value=arr[2*i+1];
dict.Add(key,value);
}
return dict;
}
There's really no easy way to do this in LINQ (And even if there were, it's certainly not going to be clear as to the intent). It's easily accomplished by a simple loop though:
// This code assumes you can guarantee your array to always have an even number
// of elements.
var array = new[] { "^BI", "connectORCL", "^CR", "connectCR" };
var dict = new Dictionary<string, string>();
for(int i=0; i < array.Length; i+=2)
{
dict.Add(array[i], array[i+1]);
}
Something like this maybe:
string[] keyValues = new string[20];
Dictionary<string, string> dict = new Dictionary<string, string>();
for (int i = 0; i < keyValues.Length; i+=2)
{
dict.Add(keyValues[i], keyValues[i + 1]);
}
Edit: People in the C# tag are damn fast...
If you have Rx as a dependency you can do:
strings
.BufferWithCount(2)
.ToDictionary(
buffer => buffer.First(), // key selector
buffer => buffer.Last()); // value selector
BufferWithCount(int count) takes the first count values from the input sequence and yield them as a list, then it takes the next count values and so on. I.e. from your input sequence you will get the pairs as lists: {"^BI", "connectORCL"}, {"^CR", "connectCR"}, the ToDictionary then takes the first list item as key and the last ( == second for lists of two items) as value.
However, if you don't use Rx, you can use this implementation of BufferWithCount:
static class EnumerableX
{
public static IEnumerable<IList<T>> BufferWithCount<T>(this IEnumerable<T> source, int count)
{
if (source == null)
{
throw new ArgumentNullException("source");
}
if (count <= 0)
{
throw new ArgumentOutOfRangeException("count");
}
var buffer = new List<T>();
foreach (var t in source)
{
buffer.Add(t);
if (buffer.Count == count)
{
yield return buffer;
buffer = new List<T>();
}
}
if (buffer.Count > 0)
{
yield return buffer;
}
}
}
It looks like other people have already beaten me to it and/or have more efficient answers but I'm posting 2 ways:
A for loop might be the clearest way to accomplish in this case...
var words = new[] { "^BI", "connectORCL", "^CR", "connectCR" };
var final = words.Where((w, i) => i % 2 == 0)
.Select((w, i) => new[] { w, words[(i * 2) + 1] })
.ToDictionary(arr => arr[0], arr => arr[1])
;
final.Dump();
//alternate way using zip
var As = words.Where((w, i) => i % 2 == 0);
var Bs = words.Where((w, i) => i % 2 == 1);
var dictionary = new Dictionary<string, string>(As.Count());
var pairs = As.Zip(Bs, (first, second) => new[] {first, second})
.ToDictionary(arr => arr[0], arr => arr[1])
;
pairs.Dump();
FYI, this is what I ended up with using a loop and implementing it as an extension method:
internal static Boolean IsEven(this Int32 #this)
{
return #this % 2 == 0;
}
internal static IDictionary<String, String> ToDictionary(this String[] #this)
{
if (!#this.Length.IsEven())
throw new ArgumentException( "Array doesn't contain an even number of entries" );
var dictionary = new Dictionary<String, String>();
for (var i = 0; i < #this.Length; i += 2)
{
var key = #this[i];
var value = #this[i + 1];
dictionary.Add(key, value);
}
return dictionary;
}
Pure Linq
Select : Project original string value and its index.
GroupBy : Group adjacent pairs.
Convert each group into dictionary entry.
string[] arr = new string[] { "^BI", "connectORCL", "^CR", "connectCR" };
var dictionary = arr.Select((value,i) => new {Value = value,Index = i})
.GroupBy(value => value.Index / 2)
.ToDictionary(g => g.FirstOrDefault().Value,
g => g.Skip(1).FirstOrDefault().Value);

Categories

Resources