Related
I have a string value:
Example:
string msg = "array('B', [255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0,
1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2,
2, 2, 2, 4, 3, 2, 2, 2, 2, 5, 4, 4, 3, 4, 6, 5, 6, 6, 6, 5, 6, 6, 6,
7, 9, 8, 6, 7, 9, 7, 6, 6, 8, 11, 8, 9, 10, 10, 10, 10, 10, 6, 8, 11,
12, 11, 10, 12, 9, 10, 10, 10, 255, 219, 0, 67, 1, 2, 2, 2, 2, 2, 2,
5, 3, 3, 5, 10, 7, 6, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10])"
I want to convert this string value to type byte[].
private byte[] data;
//after type conversion from string to byte array
Debug.Log("data in byte array is: " + data);
Output should be:
data in byte array is: [255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0, 0,
1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2,
2, 2, 2, 4, 3, 2, 2, 2, 2, 5, 4, 4, 3, 4, 6, 5, 6, 6, 6, 5, 6, 6, 6,
7, 9, 8, 6, 7, 9, 7, 6, 6, 8, 11, 8, 9, 10, 10, 10, 10, 10, 6, 8, 11,
12, 11, 10, 12, 9, 10, 10, 10, 255, 219, 0, 67, 1, 2, 2, 2, 2, 2, 2,
5, 3, 3, 5, 10, 7, 6, 7, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10]
Since the actual string msg is a very large value, I want to do this with minimum execution time possible.
Looks like a fairly simple parsing problem. We could run up a full parser for the format, but that's probably well beyond your needs.
Let's assume instead that your msg is always going to be in the format shown, starting with "array('B', [", ending with "])" and having 0 or more comma-separated decimal values. Since any option you choose is going to scan the string at least once, we can design code around doing exactly that.
The actual content contains only 4 types of characters:
digit (0-9)
comma (,)
space ( )
end-array (])
We can iterate through the characters starting at the first character after the start-array ([ at offset 11) and do something with each of those character types: ignore it, update the current value, yield the current value and/or exit.
If all of the assumptions are correct then this is a simple process. One scan through the characters, no allocations in the parser itself. Here's a sample:
IEnumerable<byte> ParseArrayMsg(string msg)
{
if (!msg.StartsWith("array('B', [") || !msg.EndsWith("])"))
yield break;
int value = 0;
for (int i = msg.IndexOf('[') + 1; i < msg.Length; i++)
{
var c = msg[i];
if (c == ',' || c == ']')
{
yield return (byte)value;
value = 0;
if (c == ']')
break;
}
else if (char.IsDigit(c))
value = value * 10 + (int)(c - '0');
else if (c != ' ')
throw new Exception($"Invalid character '{c}' at index '{i}'.");
}
}
The returned enumerable can then be processed however you like.
Your code won't actually produce the output you want, but this will:
var data = ParseArrayMsg(msg).ToArray();
Console.WriteLine($"Data in byte array is: [{string.Join(", ", data.Select(b => b.ToString()))}]");
Data in byte array is: [255, 216, 255, 224, 0, 16, 74, 70, 73, 70, 0, 1, 1, 0,
0, 1, 0, 1, 0, 0, 255, 219, 0, 67, 0, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2,
2, 4, 3, 2, 2, 2, 2, 5, 4, 4, 3, 4, 6, 5, 6, 6, 6, 5, 6, 6, 6, 7, 9, 8, 6, 7,
9, 7, 6, 6, 8, 11, 8, 9, 10, 10, 10, 10, 10, 6, 8, 11, 12, 11, 10, 12, 9, 10,
10, 10, 255, 219, 0, 67, 1, 2, 2, 2, 2, 2, 2, 5, 3, 3, 5, 10, 7, 6, 7, 10, 10,
10, 10, 10, 10, 10, 10, 10, 10, 10]
You can try like the following:
string yourString= "Your String";
// Convert a C# string to a byte array
byte[] bytes = Encoding.ASCII.GetBytes(yourString);
I try to solve next exercise:
Input: List of integers with count >= 1; some positive integer k
Output: all possible tuples of this integers with length of k;
For instance
Input: {1, 2}; k = 4
Output:
{
{1, 1, 1, 1},
{1, 1, 1, 2},
{1, 1, 2, 1},
{1, 1, 2, 2},
{1, 2, 1, 1},
{1, 2, 1, 2},
{1, 2, 2, 1},
{1, 2, 2, 2},
{2, 1, 1, 1},
{2, 1, 1, 2},
{2, 1, 2, 1},
{2, 1, 2, 2},
{2, 2, 1, 1},
{2, 2, 1, 2},
{2, 2, 2, 1},
{2, 2, 2, 2}
}
I tried to create an array that contains k copies of input list and than use Combinations:
public static IEnumerable<IEnumerable<T>> Combinations<T>(
this IEnumerable<T> elements,
int k)
{
return k == 0
? new[] { new T[0] }
: elements.SelectMany((e, i) => elements
.Skip(i + 1)
.Combinations(k - 1)
.Select(c => (new[] { e }).Concat(c)));
}
But it takes too long when k > 9. Is there an algorithm for solving this problem in a short time?
Let's get rid of recursion and have 512 items:
Code:
//TODO: you may want to declare it as IEnumerable<T[]> Combinations<T>
public static IEnumerable<IEnumerable<T>> Combinations<T>(
this IEnumerable<T> elements, int k) {
if (null == elements)
throw new ArgumentNullException(nameof(elements));
else if (k < 0)
throw new ArgumentOutOfRangeException(nameof(k));
T[] alphabet = elements.ToArray();
// Special cases
if (alphabet.Length <= 0)
yield break;
else if (k == 0)
yield break;
int[] indexes = new int[k];
do {
yield return indexes
.Select(i => alphabet[i])
.ToArray();
for (int i = indexes.Length - 1; i >= 0; --i)
if (indexes[i] >= alphabet.Length - 1)
indexes[i] = 0;
else {
indexes[i] += 1;
break;
}
}
while (!indexes.All(index => index == 0));
}
Demo:
string report = string.Join(Environment.NewLine, Combinations(new int[] { 1, 2}, 9)
.Select(line => string.Join(", ", line)));
Console.Write(report);
Outcome: (512 records)
1, 1, 1, 1, 1, 1, 1, 1, 1
1, 1, 1, 1, 1, 1, 1, 1, 2
1, 1, 1, 1, 1, 1, 1, 2, 1
1, 1, 1, 1, 1, 1, 1, 2, 2
1, 1, 1, 1, 1, 1, 2, 1, 1
1, 1, 1, 1, 1, 1, 2, 1, 2
1, 1, 1, 1, 1, 1, 2, 2, 1
1, 1, 1, 1, 1, 1, 2, 2, 2
1, 1, 1, 1, 1, 2, 1, 1, 1
...
2, 2, 2, 2, 2, 2, 1, 2, 1
2, 2, 2, 2, 2, 2, 1, 2, 2
2, 2, 2, 2, 2, 2, 2, 1, 1
2, 2, 2, 2, 2, 2, 2, 1, 2
2, 2, 2, 2, 2, 2, 2, 2, 1
2, 2, 2, 2, 2, 2, 2, 2, 2
Let's generate, say, all 2**20 == 1048576 items (k = 20), i.e. more than 1 million arrays of size 20 :
Stopwatch sw = new Stopwatch();
sw.Start();
int count = Combinations(new int[] { 1, 2 }, 20).Count();
sw.Stop();
Console.Write($"{count.ToString()} items at {sw.ElapsedMilliseconds:f0} milliseconds");
Outcome:
1048576 items at 469 milliseconds
I am trying to find position in a List based on where LINQ statement and get that item and next (x) amount. Example code:
List<int> numbers = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
numbers = numbers.Where(elt => elt == 6).Take(3).ToList();
I am trying to get back a filtered list of 6,7,8. However this is not working. Am I approaching this wrong?
Thanks in advance!
You almost got it. You just need to change the Where to a SkipWhile:
numbers = numbers.SkipWhile(elt => elt != 6).Take(3).ToList();
You have to use Where() overload that takes index of item as well and then use with indexOf():
List<int> numbers = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });
var result = numbers.Where((x, i) => i >= numbers.IndexOf(6)).Take(3);
Here's another approach which comes into play when it's possible that the number is not unique and you want all occurences including the two next followers:
List<int> numbers = new List<int>(new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 6, 7, 8, 9, 6 });
numbers = Enumerable.Range(0, numbers.Count)
.Where(index => numbers[index] == 6)
.SelectMany(index => numbers.Skip(index).Take(3))
.ToList(); // 6,7,8,6,7,8,6
I'm having trouble coming up with the most efficient algorithm to remove duplicates from List<List<int>>, for example (I know this looks like a list of int[], but just doing it that way for visual purposes:
my_list[0]= {1, 2, 3};
my_list[1]= {1, 2, 3};
my_list[2]= {9, 10, 11};
my_list[3]= {1, 2, 3};
So the output would just be
new_list[0]= {1, 2, 3};
new_list[1]= {9, 10, 11};
Let me know if you have any ideas. I would really appreciate it.
Build custom of EqualityComparer<List<int>>:
public class CusComparer : IEqualityComparer<List<int>>
{
public bool Equals(List<int> x, List<int> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<int> obj)
{
int hashCode = 0;
for (var index = 0; index < obj.Count; index++)
{
hashCode ^= new {Index = index, Item = obj[index]}.GetHashCode();
}
return hashCode;
}
}
Then you can get the result by using Distinct with custom comparer method:
var result = my_list.Distinct(new CusComparer());
Edit:
Include the index into method GetHashCode to make sure different orders will not be equal
This simple program does what you want:
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
List<List<int>> lists = new List<List<int>>();
lists.Add(new List<int> { 1, 2, 3 });
lists.Add(new List<int> { 1, 2, 3 });
lists.Add(new List<int> { 9, 10, 11 });
lists.Add(new List<int> { 1, 2, 3 });
var distinct = lists.Select(x => new HashSet<int>(x))
.Distinct(HashSet<int>.CreateSetComparer());
foreach (var list in distinct)
{
foreach (var v in list)
{
Console.Write(v + " ");
}
Console.WriteLine();
}
}
}
}
var finalList = lists.GroupBy(x => String.Join(",", x))
.Select(x => x.First().ToList())
.ToList();
You can use the LINQ Distinct overload that takes a comparer. The comparer should see if the lists are equal. Note that the default equals operations of lists won't do what you're really looking for, so the comparer will need to loop through each for you. Here's an example of such a comparer:
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
IEqualityComparer<T> itemComparer;
public SequenceComparer()
{
this.itemComparer = EqualityComparer<T>.Default;
}
public SequenceComparer(IEqualityComparer<T> itemComparer)
{
this.itemComparer = itemComparer;
}
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
if (object.Equals(x, y))
return true;
if (x == null || y == null)
return false;
return x.SequenceEqual(y, itemComparer);
}
public int GetHashCode(IEnumerable<T> obj)
{
if (obj == null)
return -1;
int i = 0;
return obj.Aggregate(0, (x, y) => x ^ new { Index = i++, ItemHash = itemComparer.GetHashCode(y) }.GetHashCode());
}
}
Update: I got the idea of using an anonymous type to make a better hash from Cuong Le's answer, and I LINQ-ified it and made it work in my class.
For small sets of data, a comparer could be useful, but if you have 1000 or more List> then trying to compare them all could begin to take a long amount of time.
I suggest that you instead use your data to build a distinct tree. The building of the tree will be much faster and when you are done you can always bring your data back into your old data structure.
I wanted to compare the performance of the answers of #Leniel Macaferi and #L.B as I wasn't sure which would be more performant, or if the difference would be significant. It turns out that the difference is very significant:
Method 1: 00:00:00.0976649 #Leniel Macaferi
Method 2: 00:00:32.0961650 #L.B
Here is the code I used to benchmark them:
public static void Main(string[] args)
{
var list = new List<List<int>> {new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,}, new List<int> {1, 2, 31, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 6}, new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 9, 10, 11, 1}, new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 9}, new List<int> {1, 2, 31, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 6, 7}, new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 9, 10, 11}, new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,}, new List<int> {1, 2, 31, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 6}, new List<int> {1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 9, 10, 11}};
var sw1 = new Stopwatch();
sw1.Start();
for (var i = 0; i < 1_000_000; i++)
{
var distinct = list.Select(x => new HashSet<int>(x)).Distinct(HashSet<int>.CreateSetComparer());
}
sw1.Stop();
Console.WriteLine($"Method 1: {sw1.Elapsed}");
var sw2 = new Stopwatch();
sw2.Start();
for (var i = 0; i < 1_000_000; i++)
{
var distinct = list.GroupBy(a => string.Join(",", a)).Select(a => a.First()).ToList();
}
sw2.Stop();
Console.WriteLine($"Method 2: {sw2.Elapsed}");
Console.ReadKey();
}
Is there a linq command that will filter out duplicates that appear in a sequence?
Example with '4':
Original { 1 2 3 4 4 4 5 6 7 4 4 4 8 9 4 4 4 }
Filtered { 1 2 3 4 5 6 7 4 8 9 4 }
Thanks.
Not really. I'd write this:
public static IEnumerable<T> RemoveDuplicates(this IEnumerable<T> sequence)
{
bool init = false;
T current = default(T);
foreach (var x in sequence)
{
if (!init || !object.Equals(current, x))
yield return x;
current = x;
init = true;
}
}
Yes there is! One-line code and one loop of the array.
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
var result = source.Where((item, index) => index + 1 == source.Length
|| item != source[index + 1]);
And according to #Hogan's advice, it can be better:
var result = source.Where((item, index) => index == 0
|| item != source[index - 1]);
More readable now i think. It means "choose the first element, and those which isn't equal to the previous one".
Similar to svick's answer, except with side effects to avoid the cons and reverse:
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
List<int> result = new List<int> { source.First() };
source.Aggregate((acc, c) =>
{
if (acc != c)
result.Add(c);
return c;
});
Edit: No longer needs the source.First() as per mquander's concern:
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
List<int> result = new List<int>();
result.Add(
source.Aggregate((acc, c) =>
{
if (acc != c)
result.Add(acc);
return c;
})
);
I think I still like Danny's solution the most.
You can use Aggregate() (although I'm not sure whether it's better than the non-LINQ solution):
var ints = new[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
var result = ints.Aggregate(
Enumerable.Empty<int>(),
(list, i) =>
list.Any() && list.First() == i
? list
: new[] { i }.Concat(list)).Reverse();
I think it's O(n), but I'm not completely sure.
If you're using .NET 4 then you can do this using the built-in Zip method, although I'd probably prefer to use a custom extension method like the one shown in mquander's answer.
// replace "new int[1]" below with "new T[1]" depending on the type of element
var filtered = original.Zip(new int[1].Concat(original),
(l, r) => new { L = l, R = r })
.Where((x, i) => (i == 0) || !object.Equals(x.L, x.R))
.Select(x => x.L);