Sort string array in special format - c#

It need to sort a string array like this to a special format. Our Array is:
input1 = new string[12]{"Active1","12","mm","Active2","17","mm","Width","25","mil","Height","20","mil"}
and our desired sort list is:
sort = new string[6]{"Serial","Width","Height","Active1","Active2","Time"}
My valid format for output is this:
Output = [{Serial,null,null},{Width,25,mil},{Height,20,mil},{Active1,12,mm},{Active2,17,mm},{Time,null,null}]
It is necessary to set null value for data that don't exist in Input Array.
I'm using this code for my purpose:
var Output = (from i in Enumerable.Range(0, input.Length / 3)
let index = Array.IndexOf(sort, input[i * 3])
where index >= 0
select ne3w string[] { input[i * 3], input[i * 3 + 1] , input[i * 3 + 2]})
.OrderBy(a => Array.IndexOf(sort, a[0])).ToArray();
but it doesn't show the values that don't exist in input Array.

I would put this into a separate method:
private static IEnumerable<string[]> TransformInput(string[] input)
{
return from key in new[] { "Serial", "Width", "Height", "Active1", "Active2", "Time" }
let keyIndex = Array.IndexOf(input, key)
let hasData = keyIndex > 1
select new[]
{
key,
hasData ? input[keyIndex + 1] : null,
hasData ? input[keyIndex + 2] : null
};
}
And then use it as follows:
var input1 = new string[12]
{ "Active1", "12", "mm", "Active2", "17", "mm", "Width", "25", "mil", "Height", "20", "mil" };
var sorted = TransformInput(input1);

You can do it with the method below:
private string[][] Sort(string[] input)
{
List<string> inputList = new List<string> ();
inputList = input.ToList<string> ();
List<string[]> sortedList = new List<string[]> ();
string[] sort = new string[]{"Serial", "Width", "Height", "Active1", "Active2", "Time"};
foreach(string key in sort)
{
if (inputList.Contains<string> (key)) {
int i = inputList.IndexOf (key);
string[] t = new string[]{inputList[i],inputList[i+1],inputList[i+2]};
sortedList.Add (t);
}
else
{
string[] t = new string[]{key,null, null};
sortedList.Add (t);
}
}
return sortedList.ToArray<string[]> ();
}
Hope it help you out!

Given the two sets of input data:
var input1 = new string[12]
{
"Active1","12","mm",
"Active2","17","mm",
"Width","25","mil",
"Height","20","mil"
};
var sort = new string[6]
{
"Serial","Width","Height","Active1","Active2","Time"
};
This worked for me:
var lookup =
input1
.Select((x, n) => new { x, n })
.ToLookup(xn => xn.n / 3)
.ToLookup(
z => z.ElementAt(0).x,
z => z.Skip(1).Select(w => w.x));
var result =
sort
.Select(x =>
new [] { x }
.Concat(lookup[x].SelectMany(z => z))
.Concat(new string[] { null, null })
.Take(3)
.ToArray())
.ToArray();
I got this result:

Building on Nitesh, but removing the need to scan input repeatedly by using a dictionary
using System.Linq; //at top of file
private static string[][] TransformInput(string[] input)
{
var sortOrder = new[] { "Serial", "Width", "Height", "Active1", "Active2", "Time" };
//dictionary pointing words to position in input
var inputDict = Enumerable.Range(0, input.Length/3)
.Select(i => i*3).ToDictionary(i => input[i]);
//Try to read position from dictionary; return nulls if fail
return sortOrder.Select(x => {
int i;
return (inputDict.TryGetValue(x, out i))
? new[]{x, input[i+1], input[i+2]}
: new[]{x, null, null};
}).ToArray();
}

I think this code is good for your problem.
static List<string[]> SortedList(string[] input)
{
var sort = new string[6] { "Serial", "Width", "Height", "Active1", "Active2", "Time" };
List<string[]> output = new List<string[]>();
for (int i = 0; i < sort.Length; i++)
{
var findIndex = input.ToList().IndexOf(sort[i]);
if (findIndex != -1)
output.Add(new string[3]
{
input[findIndex],
input[findIndex + 1],
input[findIndex + 2]
});
else
output.Add(new string[3]
{
sort[i],
null,
null
});
}
return output;
}
And now you call that method:
var input = new string[12] { "Active1", "12", "mm", "Active2", "17", "mm", "Width", "25", "mil", "Height", "20", "mil" };
var output = SortedList(input);

Related

Convert LINQ (Entity Framework) nested Any's to take N parameters

Requirement: check if a list of successive words exists in a dataset. If it does, return a boolean to show the success.
Here is my code so far with unit tests (note this is sample code only - DataSet will be with Entity Framework not just a List):
[Test]
public void PhraseSearch()
{
var DataSet = new List<Word>
{
new Word { Text = "First", Sequence = 0 },
new Word { Text = "Second", Sequence = 1 },
new Word { Text = "Third", Sequence = 2 },
new Word { Text = "Forth", Sequence = 3 },
new Word { Text = "Five", Sequence = 4 }
};
var goodSearch = new string[]{ "First", "Second", "Third" };
var badSearch = new string[] { "First", "NOTFOUND", "Third" };
// successful test for 2 words
var result = DataSet.Any(wrd1 => wrd1.Text == goodSearch[0] && DataSet.Any(wrd2 => wrd2.Text == goodSearch[1] && wrd2.Sequence == wrd1.Sequence + 1));
Assert.That(result, Is.True);
result = DataSet.Any(wrd1 => wrd1.Text == badSearch[0] &&
DataSet.Any(wrd2 => wrd2.Text == badSearch[1] && wrd2.Sequence == wrd1.Sequence + 1));
// successful test for 2 words that don't match the data
Assert.That(result, Is.False);
// successful test for 3 words
result = DataSet.Any(wrd1 => wrd1.Text == goodSearch[0] &&
DataSet.Any(wrd2 => wrd2.Text == goodSearch[1] && wrd2.Sequence == wrd1.Sequence + 1 &&
DataSet.Any(wrd3 => wrd3.Text == goodSearch[2] && wrd3.Sequence == wrd2.Sequence + 1)));
Assert.That(result, Is.True);
// test for N words
result = .....
}
I want to expand the Linq code to do N words, but i'm not sure how to do this with Linq in Entity Framework, I'm leaning towards a hard coded method for each number of words but that seems really smelly.
You could use following extension which also takes into account that there could be multiple subsets in the longer sequence and a latter one contains the whole seb-sequence:
public static bool ContainsSequence<T>(this IEnumerable<T> seq, IEnumerable<T> subSeq, EqualityComparer<T> comparer = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
IList<T> list = subSeq as IList<T> ?? new List<T>(subSeq);
IEnumerable<int> allIndexes = seq.AllIndexesOf(list.First(), comparer);
foreach (int index in allIndexes)
{
bool containsSequence = seq.Skip(index).Take(list.Count).SequenceEqual(list, comparer);
if (containsSequence)
return true;
}
return false;
}
Using this simple extension to find all indexes:
public static IEnumerable<int> AllIndexesOf<T>(this IEnumerable<T> seq, T itemToFind, EqualityComparer<T> comparer = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
int index = 0;
foreach (T item in seq)
{
if (comparer.Equals(itemToFind, item))
yield return index;
index++;
}
}
Now the remaining check is simple:
bool containsSubseq = DataSet.OrderBy(x => x.Sequence).Select(x => x.Text)
.ContainsSequence(goodSearch);
However, this works for Linq-To-Objects not for database driven LINQ providers(saw too late).
I like the subset queries, but i ended up writing a recursive any statement to solve this as below:
public void PhraseSearch()
{
var DataSet = new List<Word>
{
new Word { Text = "First", Sequence = 0 },
new Word { Text = "Second", Sequence = 1 },
new Word { Text = "Third", Sequence = 2 },
new Word { Text = "Forth", Sequence = 3 },
new Word { Text = "Five", Sequence = 4 }
};
var goodSearch1 = new[] { "Second" };
var goodSearch2 = new[] { "First", "Second"};
var goodSearch3 = new[] { "Second", "Third", "Forth" };
var badSearch = new[] { "First", "NOTFOUND", "Third" };
// successful test for 1 word
int idxTosearch = 0;
var result = DataSet.Any(wrd => wrd.Text == goodSearch1[idxTosearch] && NextAny(goodSearch1,idxTosearch + 1,DataSet, wrd));
Assert.That(result, Is.True);
//reset counter
idxTosearch = 0;
result = DataSet.Any(wrd => wrd.Text == goodSearch2[0] && NextAny(goodSearch2, idxTosearch + 1, DataSet, wrd));
// successful test for 2 words
Assert.That(result, Is.True);
//reset counter
idxTosearch = 0;
// successful test for 3 words
result = DataSet.Any(wrd => wrd.Text == goodSearch3[0] && NextAny(goodSearch3, idxTosearch + 1, DataSet, wrd));
Assert.That(result, Is.True);
// test for bad words
//reset counter
idxTosearch = 0;
result = DataSet.Any(wrd => wrd.Text == badSearch[0] && NextAny(badSearch, idxTosearch + 1, DataSet, wrd));
Assert.That(result, Is.False);
}
private static bool NextAny(string[] phraseArray, int idxToSearch, List<Word> DataSet, Word previousWord)
{
if (idxToSearch == phraseArray.Length)
{
return true;
}
return allMatches.Any(wrd => wrd.Text == phraseArray[idxToSearch] && wrd.Sequence == previousWord.Sequence + 1 && NextAny(phraseArray, idxToSearch + 1, DataSet, wrd));
}
Try this to find a subset:
public void PhraseSearch()
{
var dataSet = new[] { "First", "Second", "Third", "Forth", "Five", };
var goodSearch = new[] { "First", "Second", "Third" };
var badSearch = new[] { "First", "NOTFOUND", "Third" };
Console.WriteLine(SubsequenceMatch(dataSet, new[] { "First", "Second", "Third" }));
Console.WriteLine(SubsequenceMatch(dataSet, new[] { "First", "NOTFOUND", "Third" }));
Console.WriteLine(SubsequenceMatch(dataSet, new[] { "Second", "Third", "Forth" }));
Console.WriteLine(SubsequenceMatch(dataSet, new[] { "Second", "Third", "Forth", "Five" }));
}
public bool SubsequenceMatch<T>(T[] source, T[] match)
{
return
Enumerable
.Range(0, source.Count() - match.Count() + 1)
.Any(n => source.Skip(n).Take(match.Count()).SequenceEqual(match));
}
This gives:
True
False
True
True

Merging List with same index?

Example: if I have list 1, 2, 3 like this:
var list1 = new List<string> {"B", "S", "", "", "", "", ""};
var list2 = new List<string> {"", "", "B", "S", "", "", ""};
var list3 = new List<string> {"", "", "", "", "B", "S", ""};
So, I have found another question. i have try it.
getAct.AddRange(a.MatchedALLlist[j].AllNewActionList);
And result is
listAll = { "B","S","","","","","","","","B","S","","","","","","","","B","S","")
but I want to merge to one list like.
listAll = { "B","S","B","S","B","S","")
What should I do?
With extension method
public static IEnumerable<string> Merge<T(
this IEnumerable<string> first,
IEnumerable<string> second,
IEnumerable<string> third)
{
using (var eFirst = first.GetEnumerator())
using (var eSecond = second.GetEnumerator())
using (var eThird = third.GetEnumerator())
{
while (eFirst.MoveNext() && eSecond.MoveNext() && eThird.MoveNext())
{
var values = new[] { eFirst.Current, eSecond.Current, eThird.Current };
yield return values.Where(value => string.IsNullOrEmpty(value) == false)
.DefaultIfEmpty("")
.First();
}
}
}
By using while we ensure that merge will be done amount of time equal to amount of items in smallest collection.
Then use it
var merged = list1.Merge(list2, list3);
Another approach (actually the same) where you can use already existed LINQ extension functions:
Use FirstNonEmpty method (from #dasblinkenlight's answer) and Zip method
var merge =
list1.Zip(list2, (value1, value2) => FirstNonEmpty(value1, value2))
.Zip(list3, (value, value3) => FirstNonEmpty(value, value3));
I suppose you are sure that all lists have the same length and only one of the lists have non-empty element at each index, so you can get the expected result simply this way:
var result = list1.Select((x, i) => x + list2[i] + list3[i]).ToList();
Sometimes a simple answer is the best one.
Why not use the good old classic for loop:
var list1 = new List<string> { "B", "S", "", "", "", "", "" };
var list2 = new List<string> { "", "", "B", "S", "", "", "" };
var list3 = new List<string> { "", "", "", "", "B", "S", "" };
var list4 = new List<string>();
for (int i = 0; i < list1.Count; i++)
{
if (!string.IsNullOrWhiteSpace(list1[i]))
list4.Add(list1[i]);
else if (!string.IsNullOrWhiteSpace(list2[i]))
list4.Add(list2[i]);
else
list4.Add(list3[i]);
}
It should do what you want, as long as the lengths of all 3 lists are the same.

split a string array to a jagged object array

I want to make a string array with values of names and some numbers(which are strings)
i want to pass them into a function that will take the array and then split them into an object jagged array (1 array of strings and 1 array of ints)
the array is:
string[] str= { "toto", "the", "moto", "my", "friend","12","13","14","99","88"};
and the function looks like this:
public object[][] bloop (string[] bstr)
{
}
whats next?
Your scenario looks like bad design that can cause errors and performance issues. The better way is to change code for using generic List<> or something like that. But in your current problem you can use below code:
public object[][] bloop (string[] bstr)
{
var numbers = new List<int>();
var strings = new List<string>();
var result = new object[2][];
foreach(var str in bstr)
{
int number = 0;
if(int.TryParse(str, out number))
{
numbers.Add(number);
}
else
{
strings.Add(str);
}
}
result[0] = strings.ToArray();
result[1] = numbers.ToArray();
return result;
}
public static object[][] bloop(string[] bstr)
{
object[][] result = new object[2][] { new object[bstr.Length], new object[bstr.Length] };
int sFlag = 0, iFlag = 0, val;
foreach (string str in bstr)
if (int.TryParse(str, out val))
result[1][iFlag++] = val;
else
result[0][sFlag++] = str;
return result;
}
I agree that your requirement sounds odd and should be solved with a different approach. However, this will do what you want:
public T[][] Bloop<T>(T[] items)
{
if (items == null) throw new ArgumentNullException("items");
if (items.Length == 1) return new T[][] { items, new T[] { } };
int firstLength = (int) Math.Ceiling((double)items.Length / 2);
T[] firstPart = new T[firstLength];
Array.Copy(items, 0, firstPart, 0, firstLength);
int secondLength = (int)Math.Floor((double)items.Length / 2);
T[] secondPart = new T[secondLength];
Array.Copy(items, firstLength, secondPart, 0, secondLength);
return new T[][] { firstPart, secondPart };
}
Your sample:
string[] str= { "toto", "the", "moto", "my", "friend","12","13","14","99","88"};
string[][] result = Bloop(str);
If you need the second array as int[] you could use following:
int[] ints = Array.ConvertAll(result[1], int.Parse);
Linq solution.
You have two groups: first one has items that can be parsed to int and the second group contains all the others, so GroupBy looks quite naturally:
public Object[][] bloop(string[] bstr) {
if (null == bstr)
throw new ArgumentNullException("bstr");
int v;
return bstr
.GroupBy(x => int.TryParse(x, out v))
.OrderBy(chunk => chunk.Key) // let strings be the first
.Select(chunk => chunk.ToArray())
.ToArray();
}
Test:
string[] str = { "toto", "the", "moto", "my", "friend", "12", "13", "14", "99", "88" };
// toto, the, moto, my, friend
// 12, 13, 14, 99, 88
Console.Write(String.Join(Environment.NewLine,
bloop(str).Select(x => String.Join(", ", x))));

Find Biggest Concatenation Word in List

I have some code that doesn't work as expected. As output, I get:
Concatenation.Concatenation+ConcatWord
I have no idea how to make it work as I would like. It should output the biggest concatenation word in the array and the number of its characters. The input might be:
string[] words = {"five","fivetwo","fourfive","fourfivetwo","one","onefiveone","two","twofivefourone"}`
for which the output should be:
"fourfivetwo" with 11 characters
Here's my code
I think something is wrong with this part, but I'm not sure.:
List<ConcatWord> concatWords = new List<ConcatWord>();
for (int i = 0; i < data.Length; i++)
{
ConcatWord concatWord = new ConcatWord(i, data[i]);
for (int j = 0; j < data.Length; j++)
{
if (i != j)
{
if (data[i].Contains(data[j]) && data[i].Length > data[j].Length)
{
concatWord.words.Add(data[j]);
}
}
}
}
Try this:
string[] words = { "five", "fivetwo", "fourfive", "fourfivetwo", "one", "onefiveone", "two", "twofivefourone" };
var allCombinations = words.SelectMany(w => words, (left, right) => left + right);
var combinedWords = words.Where(w => allCombinations.Contains(w));
string longestCombinedWord = combinedWords.OrderByDescending(w => w.Length).First();
Console.WriteLine(longestCombinedWord);
Here's how you can make it shorter
string[] data = { "five","fivetwo","fivetwo","fivetwo","fourfive","fourfivetwo","one","onefiveone","two","twofivefourone" };
string longestWord = data.OrderByDescending(words => words.Length)
.Distinct()
.First();
Console.WriteLine (longestWord);
You could break it apart.
class Program
{
static void Main()
{
var searchTerms = new List<string> {"one", "two", "three", "four", "five"};
var words = new List<string>
{
"five",
"fivetwo",
"fourfive",
"fourfivetwo",
"one",
"onefiveone",
"two",
"twofivefourone"
};
var wordsAndMatches = new Dictionary<string, int>()
{
{"five", 0}, {"fivetwo", 0}, {"fourfive", 0},
{ "fourfivetwo", 0}, {"one", 0}, {"onefiveone", 0},
{"two", 0}, {"twofivefourone", 0}
};
foreach (var word in words)
{
var numberOfMatches = GetNumberOfOccurances(word, searchTerms);
wordsAndMatches[word] = numberOfMatches;
}
foreach (var wordsAndMatch in wordsAndMatches)
{
var result = string.Format("{0} contains {1} number of matches.", wordsAndMatch.Key, wordsAndMatch.Value);
Console.WriteLine(result);
}
var highestNumberOfConcatenations = wordsAndMatches.Values.OrderByDescending(x => x).FirstOrDefault();
var wordWithHighestNumberOfMatches = wordsAndMatches.FirstOrDefault(x => x.Value == highestNumberOfConcatenations);
Console.WriteLine("{0} has the highest number of matches at {1} matches.", wordWithHighestNumberOfMatches.Key, wordWithHighestNumberOfMatches.Value);
}
private static int GetNumberOfOccurances(string word, IEnumerable<string> searchTerms)
{
return searchTerms.Count(word.Contains);
}
}

Check a new list is in order compare to the old list

Compare 2 lists of strings.
var oldList = new List<string>{"aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg","hhh","iii"};
var newList = new List<string>{"aaa", "bbb", "ccc", "ddd"};//True
var newList2 = new List<string>{"bbb", "ccc", "ddd"};//True
var newList3 = new List<string>{"bbb", "ddd", "fff"};//True
var newList4 = new List<string>{"bbb", "ccc", "aaa", "ddd", "ggg", "fff"};//False
var newList5 = new List<string>{"bbb", "ccc", "aaa"};//False
How do compare them in code?
Edition:
Not allow Duplication in the new list
You can use following extension, i have tested it with all your lists:
public static bool ContainsSequenceSameOrder<T>(this IEnumerable<T> seq1, IEnumerable<T> seq2)
{
if (seq1 == null || seq2 == null) throw new ArgumentNullException();
List<T> l1 = seq1 as List<T>;
if (l1 == null) l1 = seq1.ToList();
int indexOf = 0;
foreach (T obj in seq2)
{
indexOf = l1.IndexOf(obj, indexOf);
if (indexOf == -1) return false;
indexOf++;
}
return true;
}
Old answer (question changed):
You can use SequenceEqual:
int count = Math.Min(oldList.Count, newList.Count);
bool sameOrder = oldList.Take(count).SequenceEqual(newList.Take(count));
Edit: according to your revised question and your comment you want to know if the first collection contains the second as "sub-collection". Then you can use this extension:
public static bool ContainsSubSequence<T>(this IEnumerable<T> seq1, IEnumerable<T> seq2)
{
if(seq1 == null || seq2 == null) throw new ArgumentNullException();
IList<T> l1 = seq1 as IList<T>;
IList<T> l2 = seq2 as IList<T>;
int c1 = l1 == null ? seq1.Count() : l1.Count;
int c2 = l2 == null ? seq2.Count() : l2.Count;
if (c2 > c1) return false;
for (int i = 0; i <= c1 - c2; i++)
{
bool contains = seq1.Skip(i).Take(c2).SequenceEqual(seq2);
if (contains) return true;
}
return false;
}
tested with:
var oldList = new List<string> { "foo", "aaa", "bbb", "ccc", "ddd", "eee", "fff" };
var newList = new List<string> { "aaa", "bbb", "ccc", "ddd" };
bool containsSubCollection = oldList.ContainsSubSequence(newList); // true
Compare both list up to the last item of the shorter list. If until that position all items are equal, then the newList is correct.
var oldList = new List<string>{aaa, bbb, ccc, ddd, eee, fff, ggg ,hhh ,iii};
var newList = new List<string>{bbb, ccc, aaa, ddd, ggg, fff};
int lenghtOfShorterList = Math.Min(oldList.Count, newList.Count);
for(int i = 0; i < lenghtOfShorterList ; i++)
{
if(oldList[i] != newList[i])
{
return; //list not correct
}
}
return; //list correct
If I understand you correctly, you want to know if subsequence is in the same order, regardless of whether elements go one by one:
class Program
{
static void Main(string[] args)
{
var oldList = new List<string> { "aaa", "bbb", "ccc", "ddd", "eee", "fff", "ggg", "hhh", "iii" };
var newList = new List<string> { "aaa", "bbb", "ccc", "ddd" };//True
var newList2 = new List<string> { "bbb", "ccc", "ddd" };//True
var newList3 = new List<string> { "bbb", "ddd", "fff" };//True
var newList4 = new List<string> { "bbb", "ccc", "aaa", "ddd", "ggg", "fff" };//False
var newList5 = new List<string> { "bbb", "ccc", "aaa" };//False
Console.WriteLine("Expected: true, Actual: " + IsCorrectOrder(oldList, newList));
Console.WriteLine("Expected: true, Actual: " + IsCorrectOrder(oldList, newList2));
Console.WriteLine("Expected: true, Actual: " + IsCorrectOrder(oldList, newList3));
Console.WriteLine("Expected: false, Actual: " + IsCorrectOrder(oldList, newList4));
Console.WriteLine("Expected: false, Actual: " + IsCorrectOrder(oldList, newList5));
Console.ReadKey(true);
}
public static bool IsCorrectOrder(IList<string> orderedSequence, IList<string> testedSequence)
{
var lastFoundIndex = 0;
for (var i = 0; i < testedSequence.Count; i++)
{
var testedElement = testedSequence[i];
var testedFound = false;
// finding first matching item in ordered sequence
for (var j = lastFoundIndex; j < orderedSequence.Count; j++)
{
var orderedElement = orderedSequence[j];
if (orderedElement == testedElement)
{
lastFoundIndex = j;
testedFound = true;
break;
}
}
if (testedFound)
continue;
// if there is no such element, then the order is not correct
return false;
}
return true;
}
}

Categories