I have a List of items containing either 1 or 0, I'm looking to output the items only where there are six 1's back to back in the list. So only write to the console if the item in this list is part of a group of six.
1
1
1
1
1
1
0
1
1
1
0
In the above list, the first six items would be output but the bottom set of three 1s would not as they are not part of a group of six.
Is this a job for LINQ or RegEx?
You can concatenate all values into string, then split it by zeros. From substrings select those which have at least 6 characters:
List<int> values = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
var series = String.Concat(values)
.Split(new[] { '0' }, StringSplitOptions.RemoveEmptyEntries)
.Where(s => s.Length >= 6);
For given input data series will contain single item "111111" which you can output to console.
Classic run length encoding, O(n), lazy evaluated, stack agnostic, generic for any equatable type.
public void TestRunLength()
{
var runs = new List<int>{ 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 3, 4, 4, 0, 4};
var finalGroup = RunLength(runs).FirstOrDefault(i => i.Count == 6 && i.First() == 1);
}
private IEnumerable<List<T>> RunLength<T>(IEnumerable<T> source) where T : IEquatable<T>
{
T current = default(T);
var requiresInit = true;
var list = new List<T>();
foreach (var i in source)
{
if (requiresInit)
{
current = i;
requiresInit = false;
}
if (i.Equals(current))
{
list.Add(i);
}
else
{
yield return list;
list = new List<T>{ i };
current = i;
}
}
if (list.Any())
{
yield return list;
}
}
And because it's lazy it works on infinite sequences (yes I know its not infinite, but it is large)!
public void TestRunLength()
{
var random = new Random();
var runs = Enumerable.Range(int.MinValue, int.MaxValue)
.Select(i => random.Next(0, 10));
var finalGroup = RunLength(runs)
.FirstOrDefault(i => i.Count == 6);
}
Probably it can be done with Regex too if you concatenate your numbers into a string. But I would prefer linq:
var bits = new List<int> {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0};
int bitCountPerGroup = 6;
var result = bits // (1) (2)
.Select((x,idx) => bits.Skip(idx).TakeWhile(y => y == x))
.Where(g => g.Count() == bitCountPerGroup); // (3)
foreach (var set in result)
Console.WriteLine(string.Join(" ", set));
This code gets a number-set for each number by starting from the number (1) and taking the next numbers as long as they are equal (2). Then filter the groups and gets only those groups which have 6 numbers (3).
If for example your list is of an unknown size,or better,you do not know the items in it you could do this recursive example(note that i placed more zeros so it would fetch 2 sets of data,it works with yours also),and pass to the method the amout to group by:
//this is the datastructure to hold the results
static List<KeyValuePair<string, List<int>>> Set = new List<KeyValuePair<string, List<int>>>();
private static void GetData(List<int> lst, int group)
{
int count = 1;
int pivot = lst.First();
if (lst.Count < group)
{
return;
}
else
{
foreach (int i in lst.Skip(1))
{
if (i == pivot)
{
count++;
}
else if (count == group)
{
Set.Add(new KeyValuePair<string, List<int>>("Set of items " + pivot, lst.Take(count).ToList()));
GetData(lst.Skip(count).ToList(), group);
break;
}
else
{
GetData(lst.Skip(count).ToList(), group);
break;
}
}
}
}
Then in Main():
static void Main(string[] args)
{
List<int> test = new List<int> { 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0 };
GetData(test, 6);
foreach (var item in Set)
{
Console.WriteLine("\t" + item.Key);
foreach (var subitem in item.Value)
{
Console.WriteLine(subitem);
}
}
}
Related
Suppose that I have a list of integer or whatever
List<int> motherlist = { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 }
Console.WriteLine(children.Count); // 10
I would like to find all duplicates and not remove them from the list but to distribute them across other lists so the final count of all childrens should be the same as motherlist:
List<List<int>> children = { { 1, 2, 5, 7, 6 }, { 1, 2 }, { 1, 2 }, { 2 }}
Console.WriteLine(children.Sum(l => l.Count())); // 10 same as mother
I tried so far a brute force approach by looping through all elements of mother, comparing the elements with all other elements and to check for duplicates, If duplicate found I add it to a list of buckets (List of Lists) and so forth until the last elements.
But the brute force approach takes 7 CPU seconds for only a mother list of 300 items.
I imagine that if I had 1000 items this would take forever.
Is there a faster way to do this in C# .NET ?
I suggest grouping duplicates and then loop taking into account size of the groups:
public static IEnumerable<List<T>> MyDo<T>(IEnumerable<T> source,
IEqualityComparer<T> comparer = null) {
if (null == source)
throw new ArgumentNullException(nameof(source));
var groups = new Dictionary<T, List<T>>(comparer ?? EqualityComparer<T>.Default);
int maxLength = 0;
foreach (T item in source) {
if (!groups.TryGetValue(item, out var list))
groups.Add(item, list = new List<T>());
list.Add(item);
maxLength = Math.Max(maxLength, list.Count);
}
for (int i = 0; i < maxLength; ++i) {
List<T> result = new List<T>();
foreach (var value in groups.Values)
if (i < value.Count)
result.Add(value[i]);
yield return result;
}
}
Demo:
int[] source = new int[] { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var result = MyDo(source).ToList();
string report = string.Join(Environment.NewLine, result
.Select(line => $"[{string.Join(", ", line)}]"));
Console.Write(report);
Outcome:
[1, 2, 5, 7, 6]
[1, 2]
[1, 2]
[2]
Stress Demo:
Random random = new Random(1234); // seed, the results to be reproducible
// We don't want 1000 items be forever; let's try 1_000_000 items
int[] source = Enumerable
.Range(1, 1_000_000)
.Select(x => random.Next(1, 1000))
.ToArray();
Stopwatch sw = new Stopwatch();
sw.Start();
var result = MyDo(source).ToList();
sw.Stop();
Console.WriteLine($"Time: {sw.ElapsedMilliseconds} ms");
Outcome: (may vary from workstation to workstation)
Time: 50 ms
I would GroupBy the elements of the list, and then use the count of elements to know the number of sublists an element has to be added in
List<int> motherlist = new List<int> { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var childrens = motherlist.GroupBy(x => x).OrderByDescending(x => x.Count());
var result = new List<List<int>>();
foreach (var children in childrens)
{
for (var i = 0; i < children.Count(); i++)
{
if (result.Count() <= i) result.Add(new List<int>());
result[i].Add(children.Key);
}
}
Console.WriteLine("{");
foreach (var res in result)
{
Console.WriteLine($"\t{{ { string.Join(", ", res) } }}");
}
Console.WriteLine("}");
This outputs :
{
{ 2, 1, 5, 7, 6 }
{ 2, 1 }
{ 2, 1 }
{ 2 }
}
Just a quick shot, but it seems to work quite well...
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
List<int> motherlist = new List<int> { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var rnd = new Random(1);
for (int i = 0; i < 1000; i++)
{
motherlist.Add(rnd.Next(1, 200));
}
var resultLists = new List<IEnumerable<int>>();
while (motherlist.Any())
{
var subList = motherlist.Distinct().OrderBy(x => x).ToList();
subList.ForEach(x => motherlist.Remove(x));
resultLists.Add(subList);
}
}
}
}
You can use a Dictionary<int, int> to keep track of the number of occurrences of each element and build the child lists in a single iteration with O(n) time complexity(most of the time) and without any LINQ:
var motherlist = new List<int>() { 1, 1, 2, 5, 7, 2, 2, 2, 6, 1 };
var counts = new Dictionary<int, int>();
var children = new List<List<int>>();
foreach(var element in motherlist)
{
counts.TryGetValue(element, out int count);
counts[element] = ++count;
if (children.Count < count)
{
children.Add(new List<int>() { element });
}
else
{
children[count - 1].Add(element);
}
}
OUTPUT
{ 1, 2, 5, 7, 6 }
{ 1, 2 }
{ 2, 1 }
{ 2 }
There is a list of short. The values of it doesn't matter like:
List<short> resultTemp = new List<short>{1,2,3,4,5,6,7,8,9...};
This code should reduse the result list count by removing each Nth item from it.
Example 1:
List<short>{1,2,3,4,5,6,7,8,9,10}.Count == 10;
var targetItemsCount = 5;
result should be {1,3,5,7,9} and result.Count should be == 5
Example 2:
List<short>{1,2,3,4,5,6,7,8,9}.Count == 9;
var targetItemsCo:nt = 3;
result should be {1,4,7} and result.Count should be == 3
But it should stop to remove it, somewhere for make result count equal targetItemsCount (42 in this code, but its value else doesn't matter).
The code is:
var currentItemsCount = resultTemp.Count;
var result = new List<short>();
var targetItemsCount = 42;
var counter = 0;
var counterResettable = 0;
if (targetItemsCount < currentItemsCount)
{
var reduceIndex = (double)currentItemsCount / targetItemsCount;
foreach (var item in resultTemp)
{
if (counterResettable < reduceIndex ||
result.Count + 1 == currentItemsCount - counter)
{
result.Add(item);
counterResettable++;
}
else
{
counterResettable = 0;
}
counter++;
}
}
And the resault.Count in this example equals 41, but should be == targetItemsCount == 42;
Ho do I remove each N item in List untill List.Count more then target value with C#?
If my understanding is correct:
public static void run()
{
var inputs =
new List<Input>{
new Input{
Value = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 },`
TargetCount = 5, ExpectedOutput= new List<int>{1,3,5,7,9}
},
new Input{
Value = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
TargetCount = 3, ExpectedOutput= new List<int>{1,4,7}
},
};
foreach (var testInput in inputs)
{
Console.WriteLine($"# Input = [{string.Join(", ", testInput.Value)}]");
var result = Reduce(testInput.Value, testInput.TargetCount);
Console.WriteLine($"# Computed Result = [{string.Join(", ", result)} ]\n");
}
}
static List<int> Reduce(List<int> input, int targetItemsCount)
{
while (input.Count() > targetItemsCount)
{
var nIndex = input.Count() / targetItemsCount;
input = input.Where((x, i) => i % nIndex == 0).ToList();
}
return input;
}
class Input
{
public List<int> ExpectedOutput;
public List<int> Value;
public int TargetCount;
}
Result :
Input = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Computed Result = [1, 3, 5, 7, 9 ]
Input = [1, 2, 3, 4, 5, 6, 7, 8, 9]
Computed Result = [1, 4, 7 ]
To guarantee you get the expected number of selected items:
double increment = Convert.ToDouble(resultTemp.Count) / targetItemsCount;
List<short> result = Enumerable.Range(0, targetItemsCount).
Select(x => resultTemp[(int)(x * increment)]).
ToList();
Note that in the following case
List<short>{1,2,3,4,5,6,7,8,9}.Count == 9;
var targetItemsCount = 6;
The result will be [1, 2, 4, 5, 7, 8], i.e. rounding the index down when needed
Also, you'll need to add validation (targetItemsCount > 0, targetItemsCount < resultTemp.Count...)
Link to Fiddle
Give this a try:
var resultTemp = Enumerable.Range(1, 9).ToList();
var targetItemsCount = 3;
var roundingError = resultTemp.Count % targetItemsCount;
var reduceIndex = (resultTemp.Count - roundingError) / targetItemsCount;
List<int> result;
if (reduceIndex <= 1)
result = resultTemp.Take(targetItemsCount).ToList();
else
result = resultTemp.Where((a, index) => index % reduceIndex == 0).Take(targetItemsCount).ToList();
Tried it with your given example, also gave 42 a spin with a list of 1 to 100 it will remove every 2nd item till it reaches 42, so the last entry in the list would be 83.
As I said, give it a try and let me know if it fits your requirement.
I have this simple array with the following items:
[4, 3, 1, 1, 0, 0]
I want to sort it on this way:
[4, 3, 1, 0, 1, 0]
As you notice, I want to sort it on descending order. But on this case, there are two sets of array that was sorted on descending order:
4, 3, 1, 0 and 1, 0
Which produce the output:
[4, 3, 1, 0, 1, 0]
I tried to do this using Group By:
var result = arrayInput.GroupBy(c => c).Select(a => a.OrderBy(d => d).First());
But this produces only 4, 3, 1, 0 and I need to append the not selected integers as sorted so that it will be:
[4, 3, 1, 0, 1, 0]
Here's how I'd do that. Basically you want to try putting each number into a group of sets. The first one that doesn't already have that number is the one it actually goes in and if none have it then you add a new set. In this way the first set will have all unique numbers, the second with have all numbers that are duplicated as least once, and so on. Then at the end you return items for each of the sets ordering each set as you go.
public static IEnumerable<int> SetSort(this IEnumerable<int> nums) {
var sets = new List<HashSet<int>>();
foreach(var num in nums) {
bool added = false;
foreach(var set in sets) {
added = set.Add(num);
if(added) break;
}
if(!added){
sets.Add(new HashSet<int> { num });
}
}
foreach(var set in sets) {
foreach(var num in set.OrderByDescending(x => x)) {
yield return num;
}
}
}
As usually Aggregate extension method can do everything
var data = new[] { 1, 2, 3, 4, 1, 0, 1, 0 };
var result =
data.GroupBy(i => i)
.OrderByDescending(group => group.Key)
.Aggregate(new { Keys = new List<int>(), Duplicates = new List<int>() },
(lists, group) =>
{
lists.Keys.Add(group.Key);
var duplicates = Enumerable.Repeat(group.Key, group.Count() - 1);
lists.Duplicates.AddRange(duplicates);
return lists;
},
lists => lists.Keys.Concat(lists.Duplicates));
// result is new[] { 4, 3, 2, 1, 0, 1, 1, 0 };
Approach with immutable collections
private IEnumerable<int> SortFunc(IEnumerable<int> data)
{
var ordered =
data.GroupBy(i => i)
.OrderByDescending(group => group.Key)
.Select(group => new
{
Key = group.Key,
Duplicates = group.Skip(1)
});
foreach (var key in ordered.Select(group => group.Key))
{
yield return key;
}
foreach (var value in ordered.SelectMany(group => group.Duplicates))
{
yield return value;
}
}
I have some problems. I have 2 list such as:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
⇒ True result is: {1, 3, 0}
I would like to get the first index of numbers in secondList that exists in firstList. I used list.BinarySearch() but the result was {2, 3, 0}.
List<int> firstList = new List<int> { 1, 2, 2, 3, 5};
List<int> secondList = new List<int> { 2, 3, 1 };
var output = secondList.Select(item => firstList.IndexOf(item)); // [1 , 3 , 0]
You can replace the IndexOf with a BinarySearch logic, but BinarySearch returns the first matched element index, so you won't get the lowest number, IndexOf does return the lowest matching index.
The problem is that when the list contains duplicate values as in your case, the BinarySearch method will return the index of any of the matching values (non deterministic).
To get the desired result, you could create and use a custom extension method like this:
public static class ListExtensions
{
public static int BinarySearchFirst<T>(this List<T> source, T item, IComparer<T> comparer = null)
{
if (comparer == null) comparer = Comparer<T>.Default;
int index = source.BinarySearch(item, comparer);
while (index > 0 && comparer.Compare(source[index], source[index - 1]) == 0)
index--;
return index;
}
}
Sample usage:
var result = secondList.Select(x => firstList.BinarySearchFirst(x)).ToList();
// { 1, 3, 0 }
C++ has a standard library function for this called lower_bound().
Here's a C# implementation. This is useful if you are searching large collections:
public static int LowerBound<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int left = first;
int right = last;
while (left < right)
{
int mid = left + (right - left) / 2;
var middle = values[mid];
if (middle.CompareTo(target) < 0)
left = mid + 1;
else
right = mid;
}
return left;
}
That doesn't return -1 for elements that it doesn't find, so to fix that we can wrap it like so:
public static int LowerBoundOrMinusOne<T>(IList<T> values, T target, int first, int last)
where T : IComparable<T>
{
int result = LowerBound(values, target, first, last);
if (result >= last || result < first || values[result].CompareTo(target) != 0)
return -1;
return result;
}
Here is how you use it:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1 };
List<int> result = secondList
.Select(value => LowerBoundOrMinusOne(firstList, value, 0, firstList.Count))
.ToList();
Console.WriteLine(string.Join(", ", result));
Of course, this is mainly of benefit to large lists because it has an O(Log2(N)) rather than an O(N) complexity.
Iterate through second array and get index of element in first array:
foreach (int item in secondList)
{
Console.WriteLine(firstList.IndexOf(item));
}
If you have a large firstList and so you have to use BinarySearch try amending it: find out the item (which is not guaranteed to be the leftmost one) by BinarySearch, then move to the left while having read the same item:
List<int> firstList = new List<int> { 1, 2, 2, 3, 5 };
List<int> secondList = new List<int> { 2, 3, 1, 123 };
var result = secondList
.Select(item => firstList.BinarySearch(item))
.Select(index => index < 0 ? -1 : Enumerable
.Range(0, index + 1) // we have to scan [0..index] at the worst case
.Select(i => index - i) // scan in reverse
.TakeWhile(i => firstList[index] == firstList[i]) // take while items are the same
.Last()); // finally, we want the last item
Test
// 1, 3, 0, -1
Console.Write(String.Join(", ", result));
I'm looking for a way to prevent repeating items in a list but still preserve the order.
For example
1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4
should become
1, 2, 3, 4, 1, 2, 3, 4
I've done it quite inelegantly using a for loop, checking the next item as follows
public static List<T> RemoveSequencialRepeats<T>(List<T> input)
{
var result = new List<T>();
for (int index = 0; index < input.Count; index++)
{
if (index == input.Count - 1)
{
result.Add(input[index]);
}
else if (!input[index].Equals(input[index + 1]))
{
result.Add(input[index]);
}
}
return result;
}
Is there a more elegant way to do this, preferably with LINQ?
You can create extension method:
public static IEnumerable<T> RemoveSequentialRepeats<T>(
this IEnumerable<T> source)
{
using (var iterator = source.GetEnumerator())
{
var comparer = EqualityComparer<T>.Default;
if (!iterator.MoveNext())
yield break;
var current = iterator.Current;
yield return current;
while (iterator.MoveNext())
{
if (comparer.Equals(iterator.Current, current))
continue;
current = iterator.Current;
yield return current;
}
}
}
Usage:
var result = items.RemoveSequentialRepeats().ToList();
You can also use pure LINQ:
List<int> list = new List<int>{1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4};
var result = list.Where((x, i) => i == 0 || x != list[i - 1]);
If you really really hate the world, pure LINQ:
var nmbs = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4, 5 };
var res = nmbs
.Take(1)
.Concat(
nmbs.Skip(1)
.Zip(nmbs, (p, q) => new { prev = q, curr = p })
.Where(p => p.prev != p.curr)
.Select(p => p.curr));
But note that you'll need to enumerate (at least partially) the enumerable 3 times (the Take, the "left" part of Zip, the first parameters of Zip). This method is slower than building a yield method or doing it directly.
Explanation:
You take the first number (.Take(1))
You take all the numbers from the second (.Skip(1)) and pair it with all the numbers (.Zip(nmbs). We will call curr the numbers from the first "collection" and prev the numbers from the second "collection" ((p, q) => new { prev = q, curr = p })). You then take only the numbers that are different from the previous number (.Where(p => p.prev != p.curr)) and from these you take the curr value and discard the prev value (.Select(p => p.curr))
You concat these two collections (.Concat()
you could write simple LINQ :
var l = new int[] { 1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
var k = new Nullable<int>();
var nl = l.Where(x => { var res = x != k; k = x; return res; }).ToArray();
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
or pythonic (well, my best try) way:
l.Zip(l.Skip(1), (x, y) => new[] { x, y })
.Where(z => z[0] != z[1]).Select(a => a[0])
.Concat(new[] { l[l.Length - 1] }).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
the simplest one (edit: haven't seen that it already suggested by King King)
l.Where((x, i) => i == l.Length - 1 || x != l[i + 1]).ToArray()
int[8] { 1, 2, 3, 4, 1, 2, 3, 4 }
If you want LINQ statement that do not rely on captured value of result inside the call you'll need some construct with aggregate as it is the only method that carries value along with operation. I.e. based on Zaheer Ahmed's code:
array.Aggregate(new List<string>(),
(items, element) =>
{
if (items.Count == 0 || items.Last() != element)
{
items.Add(element);
}
return items;
});
Or you can even try to build list without if:
array.Aggregate(Enumerable.Empty<string>(),
(items, element) => items.Concat(
Enumerable.Repeat(element,
items.Count() == 0 || items.Last() != element ? 1:0 ))
);
Note to get reasonable performance of above samples with Aggregate you'd need to also carry last value (Last will have to iterate whole sequence on each step), but code that carries 3 values {IsEmpty, LastValue, Sequence} in a Tuple is very strange looking. These samples are here for entertaining purposes only.
One more option is to Zip array with itself shifted by 1 and return elements that are not equal...
More practical option is to build iterator that filters values:
IEnumerable<string> NonRepeated(IEnumerable<string> values)
{
string last = null;
bool lastSet = false;
foreach(var element in values)
{
if (!lastSet || last != element)
{
yield return element;
}
last = element;
lastSet = true;
}
}
check if last of new list and current item is not same then add to new list:
List<string> results = new List<string>();
results.Add(array.First());
foreach (var element in array)
{
if(results[results.Length - 1] != element)
results.Add(element);
}
or using LINQ:
List<int> arr=new List<int>(){1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 };
List<int> result = new List<int>() { arr.First() };
arr.Select(x =>
{
if (result[result.Length - 1] != x) result.Add(x);
return x;
}).ToList();
Do have proper validation for null object.
Try this:
class Program
{
static void Main(string[] args)
{
var input = "1, 2, 3, 4, 4, 4, 1, 1, 2, 3, 4, 4 ";
var list = input.Split(',').Select(i => i.Trim());
var result = list
.Select((s, i) =>
(s != list.Skip(i + 1).FirstOrDefault()) ? s : null)
.Where(s => s != null)
.ToList();
}
}
Here the code you need :
public static List<int> RemoveSequencialRepeats(List<int> input)
{
var result = new List<int>();
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
return result;
}
The LINQ magic is:
result.Add(input.First());
result.AddRange(input.Where(p_element => result.Last() != p_element);
Or you can create extension method like this:
public static class Program
{
static void Main(string[] args)
{
List<int> numList=new List<int>(){1,2,2,2,4,5,3,2};
numList = numList.RemoveSequentialRepeats();
}
public static List<T> RemoveSequentialRepeats<T>(this List<T> p_input)
{
var result = new List<T> { p_input.First() };
result.AddRange(p_input.Where(p_element => !result.Last().Equals(p_element)));
return result;
}
}
If you feel like referencing an F# project you can write
let rec dedupe = function
| x::y::rest when x = y -> x::dedupe rest
| x::rest -> x::dedupe rest
| _ -> []