Reputation: 4848
Another program is passing my program a list of objects. These objects have a property called Title
and in this list, one of the objects will have a title that matches the first object's title. I want to separate this list into two lists, using the second object that has the same title as the first as the flag to write the remaining objects to another list. I've written a small example program that indicates what I'm trying to accomplish. I'm hoping there's someone out there that can point me in the right direction.
static void Main(string[] args)
{
var names = new string[] {"Frank", "Jules", "Mark", "Allan", "Frank", "Greg", "Tim"};
var listA = new List<string>();
var listB = new List<string>();
foreach (var name in names)
{
// Add all names to listA, until name is the next "Frank".
// If name is the next "Frank" add name and all remaining
// names to listB.
// So, listA would contain "Frank", "Jules", "Mark", "Allan"
// listB would contain "Frank", "Greg", "Tim"
}
}
Upvotes: 1
Views: 3457
Reputation: 7830
Another alternative solution (for the given example input) would be to use Enumerable.Skip to skip the first element and then use the Enumerable.SkipWhile method to take the first list and the List.Remove method to retrieve the second list after the split word (TODO: add exception handling, for example if the list is empty):
public static class ProjectExtensions
{
public static List<String> SplitTo(this List<String> source, String word)
{
// take first occurrence
var result = new List<String> { source.First() };
// skip already added (first item) and take all until search phrase
result.AddRange(source.Skip(1).TakeWhile (s => s != word));
return result;
}
public static List<String> SkipTo(this List<String> source, String word)
{
var result = source;
// skip all until search phrase and take the rest
result.RemoveRange(0, source.SplitTo(word).Count);
return result;
}
}
The usage looks like this:
var names = new List<String> {"Frank", "Jules", "Mark", "Allan", "Frank", "Greg", "Tim"};
var listA = names.SplitTo("Frank");
var listB = names.SkipTo("Frank");
foreach (var a in listA)
{
Console.WriteLine(a);
}
Console.WriteLine("---------");
foreach (var b in listB)
{
Console.WriteLine(b);
}
And the output is:
Frank
Jules
Mark
Allan
---------
Frank
Greg
Tim
Upvotes: 1
Reputation: 12807
Another option is to use aggregation. The following will split original set into n-sets.
var names = new[] { "Frank", "Jules", "Mark", "Allan", "Frank", "Greg", "Tim", "Frank", "Anna" };
string separator = names.First();
var aggregated = names.Aggregate(new List<List<string>>(), (arr, s) =>
{
if (s == separator)
arr.Add(new List<string> { s });
else
arr.Last().Add(s);
return arr;
});
And if you want strictly 1 or 2 sets, then the following code will work:
var names = new[] { "Frank", "Jules", "Mark", "Allan", "Frank", "Greg", "Tim", "Frank", "Anna" };
string separator = names.First();
var aggregated = names.Aggregate(new List<List<string>>(), (arr, s) =>
{
if (s == separator && arr.Count < 2)
arr.Add(new List<string> { s });
else
arr.Last().Add(s);
return arr;
});
var listA = aggregated[0];
var listB = aggregated.Skip(1).FirstOrDefault();
Upvotes: 1
Reputation: 101614
Here would be how I would approach it (thought it uses LINQ, you can do it without):
void Main()
{
var names = new string[] {"Frank", "Jules", "Mark", "Allan", "Frank", "Greg", "Tim"};
var listA = new List<string>();
var listB = new List<string>();
// I'm not entirely sure where this comes from other than it's always
// the first element in the "names" list.
var needle = names[0]; // "Frank"
// this finds the location of the second "Frank"
var splitLocation = Array.IndexOf(names, needle, 1);
// here we grab the first elements (up to the splitLocation)
listA = names.Take(splitLocation).ToList();
// here we grab everything past the splitLocation
// (starting with the second "Frank")
listB = names.Skip(splitLocation).ToList();
}
Upvotes: 6
Reputation: 5449
Try this algorithm.
Create a boolean flag with a value of false.
Iterate through the names:
If the current name matches the first name:
Set the flag to true
If the flag is true:
Add the current name to the second list
Else:
Add the current name to the first list
Upvotes: 1