I have string:
string t = "{ { 1, 3, 23 } , { 5, 7, 9 } , { 44, 2, 3 } }"
Can I make a string matrix using string input like this?
I can't simply assign:
int [ , ] matrix = t
Is there some function I could use or do I have to split my string in some way?
PS: 't' string could have various number of rows and columns.
This should serve your purpose
string t = "{ { 1, 3, 23 } , { 5, 7, 9 } , { 44, 2, 3 } }";
var cleanedRows = Regex.Split(t, #"}\s*,\s*{")
.Select(r => r.Replace("{", "").Replace("}", "").Trim())
.ToList();
var matrix = new int[cleanedRows.Count][];
for (var i = 0; i < cleanedRows.Count; i++)
{
var data = cleanedRows.ElementAt(i).Split(',');
matrix[i] = data.Select(c => int.Parse(c.Trim())).ToArray();
}
I came up with something rather similar but with some test cases you might want to think about - hope it helps :
private void Button_Click(object sender, RoutedEventArgs e)
{
int[][] matrix;
matrix = InitStringToMatrix("{ { 1, 3, 23 } , { 5, 7, 9 } , { 44, 2, 3 } }");
matrix = InitStringToMatrix("{ {0,1, 2 ,-3 ,4}, {0} }");
matrix = InitStringToMatrix("{} ");
matrix = InitStringToMatrix("{ {}, {1} } ");
matrix = InitStringToMatrix("{ { , 1,2,3} } ");
matrix = InitStringToMatrix("{ {1} ");
matrix = InitStringToMatrix("{ {1}{2}{3} }");
matrix = InitStringToMatrix(",,,");
matrix = InitStringToMatrix("{1 2 3}");
}
private int[][] InitStringToMatrix(string initString)
{
string[] rows = initString.Replace("}", "")
.Split('{')
.Where(s => !s.Trim().Equals(String.Empty))
.ToArray();
int [][] result = new int[rows.Count()][];
for (int i = 0; i < rows.Count(); i++)
{
result[i] = rows[i].Split(',')
.Where(s => !s.Trim().Equals(String.Empty))
.Select(val => int.Parse(val))
.ToArray();
}
return result;
}
If you like optimizations here's a solution with only one expression:
string text = "{ { 1, 3, 23 } , { 5, 7, 9 } , { 44, 2, 3 } }";
// remove spaces, makes parsing easier
text = text.Replace(" ", string.Empty) ;
var matrix =
// match groups
Regex.Matches(text, #"{(\d+,?)+},?").Cast<Match>()
.Select (m =>
// match digits in a group
Regex.Matches(m.Groups[0].Value, #"\d+(?=,?)").Cast<Match>()
// parse digits into an array
.Select (ma => int.Parse(ma.Groups[0].Value)).ToArray())
// put everything into an array
.ToArray();
Based on Arghya C's solution, here is a function which returns a int[,] instead of a int[][] as the OP asked.
public int[,] CreateMatrix(string s)
{
List<string> cleanedRows = Regex.Split(s, #"}\s*,\s*{")
.Select(r => r.Replace("{", "").Replace("}", "").Trim())
.ToList();
int[] columnsSize = cleanedRows.Select(x => x.Split(',').Length)
.Distinct()
.ToArray();
if (columnsSize.Length != 1)
throw new Exception("All columns must have the same size");
int[,] matrix = new int[cleanedRows.Count, columnsSize[0]];
string[] data;
for (int i = 0; i < cleanedRows.Count; i++)
{
data = cleanedRows[i].Split(',');
for (int j = 0; j < columnsSize[0]; j++)
{
matrix[i, j] = int.Parse(data[j].Trim());
}
}
return matrix;
}
Related
I am trying to extract subsets but not all of them, just which is a neighbour. The simple example below;
Input : "123456789"
Result :
3.Level Subset: out3 : [123,234,345,456,567,678,789]
..
5.Level Subset : out5 :[12345,23456,34567,45678,56789]
..
8.Level Subset: out8 :[12345678,23456789]
result = [out1,..,out5,..out8]
If there is a cool solution for this and if it can be string operation it will be good.
Many thanks
You'll need to do additional error checks to see if level is longer than the length of the string etc.
public static void Main(string[] args)
{
var results = FindSubsets("123456789", 3);
Console.Read();
}
public static List<string> FindSubsets(string data, int level)
{
if (level > data.Length || level < 1)
return null;
var results = new List<string>();
for (int i = 0; i < data.Length - level + 1; i++)
{
results.Add(data.Substring(i, level));
}
return results;
}
Edit:
Added string length check against level.
Edit2:
If you want to find subsets of certain length, you can do something like the following. Create a List<int> with all the levels you want to find subsets of, then repeatedly call the function. For example, let's say you want to find subsets for levels 3, 5, and 8. Then:
var data = "123456789";
var levels = new List<int>() { 3, 5, 8 };
var results = new List<List<string>>();
foreach(var level in levels)
{
results.Add(FindSubsets(data, level));
}
public IEnumerable<string> GetSubsets(string input, int length)
{
if (string.IsNullOrEmpty(input))
throw new ArgumentException("Invalid string");
if (length <= 0)
throw new ArgumentException("Length must be greater than 0.");
if (length > input.Length)
throw new ArgumentException("The desired set length is longer than the string");
for(int i = 0; i<=input.Length - length;i++)
{
yield return input.Substring(i, length);
}
}
Yes this can be done using Regex:
string input = "123456789";
int size = 3; // set to whatever
MatchCollection split = Regex.Matches(input, #$"\d{{size}}");
string delimited = string.Join(",", split);
Or as an extension method:
public static string Delimit(this string s, int size)
{
MatchCollection split = Regex.Matches(input, #$"\d{{size}}");
return delimited = string.Join(",", split);
}
I basically just brute forced the answer:
import java.util.ArrayList;
public class MyClass {
public static ArrayList<ArrayList<String>> getAllSubets(String input){
ArrayList<ArrayList<String>> list = new ArrayList<ArrayList<String>>();
ArrayList<String> out = new ArrayList<String>();
for(int i= input.length(); i>=0; i--){
int z = i;
for(int j = 0; j+i<input.length() ;j++){
z++;
out.add(input.substring(j, z));
}
}
list.add(out);
return list;
}
public static void main(String args[]) {
System.out.println(MyClass.getAllSubets("123456789"));
}
}
Output:
[[123456789, 12345678, 23456789, 1234567, 2345678, 3456789, 123456, 234567, 345678, 456789, 12345, 23456, 34567, 45678, 56789, 1234, 2345, 3456, 4567, 5678, 6789, 123, 234, 345, 456, 567, 678, 789, 12, 23, 34, 45, 56, 67, 78, 89, 1, 2, 3, 4, 5, 6, 7, 8, 9]]
This was a fun question to do its easy to get tripped up with the indexes.
Finding all Subsets:
var Input = "123456789";
var Result = Enumerable
.Range(1, Input.Length)
.Select(i => Enumerable
.Range(0, Input.Length - i + 1)
.Select(j => Input.Substring(j, i)))
.ToList();
To restrain to only some levels, like 3, 5, or 8, simply filter before performing the work, as is done below:
var Levels = new List<int>{ 3, 5, 8 };
var Input = "123456789";
var Result = Enumerable
.Range(1, Input.Length)
.Where(i => Levels.Contains(i)) // Filter the levels you need before constructing the subsets.
.Select(i => Enumerable
.Range(0, Input.Length - i + 1)
.Select(j => Input.Substring(j, i)))
.ToList();
Results:
123, 234, 345, 456, 567, 678, 789
12345,
23456,
34567,
45678,
56789
12345678,
23456789
This question already has answers here:
LINQ to find series of consecutive numbers
(6 answers)
Closed 5 years ago.
I have a List of id's (int) like
[1,2,3,5,7,8,11,13,14..]
is there a fast/smart way (linq?) to get all id's or if possible, the ranges?
The result should be like [1-3, 5, 7-8, 11, 13-14].
Sure, it's easy to loop and count the int value to get the result but I'm sure there must be a easier way to do this.
disclaimer this is very slow on big list, you should do a distinct too
this should do the trick
static void Main(string[] args)
{
//a list with a possible of duplicate
var theList = (new int[] { 1, 2, 3, 5, 7, 8, 11, 13, 14, 13 }).OrderBy(x => x).ToList();
var step1 = theList.Select((a, b) => theList.Skip(b).TakeWhile((x, y) => a == x || theList[b + y] - 1 == theList[b + y - 1]));
var step2 = step1.GroupBy(x => x.Last())
.Select(x => x.SelectMany(y => y).Distinct())
.Select(x => x.Count() > 1 ? string.Format("{0}-{1}", x.First(), x.Last()) : x.First().ToString());
var result = string.Format("[{0}]", string.Join(", ", step2));
}
Old school way with a single for loop.
try
{
List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };
int istart = i[0];
bool flag = false;
// Use StringBuilder
for(int index = 0;index<i.Count-1;index++)
{
if ((i[index] + 1) == i[index + 1])
{
flag = true;
continue;
}
else
{
if (!flag)
Console.Write(istart);
else
Console.Write(istart + "-" + i[index]);
Console.Write(",");
flag = false;
istart = i[index + 1];
}
}
if (istart + 1 == i[i.Count - 1])
Console.Write(istart + "-" + i[i.Count - 1]);
else
Console.WriteLine(istart);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine();
Console.WriteLine("Done");
Console.Read();
Input List<int> i = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };
Input List<int> i = new List<int>() { 1, 2, 3, 4, 5, 7, 8, 11, 13, 13 };
Input List<int> i = new List<int>() { 1, 4, 5, 7, 8, 9, 2, 13, 15, 17 };
Considering a ordered list and unique ids, I think the simplest approach is to use classic for and while
List<int> ids = new List<int>() { 1, 2, 3, 5, 7, 8, 11, 13, 14 };
int i = 0;
bool isrange;
for(i=0;i<ids.Count;i++)
{
isrange = false;
Console.Write(ids[i]);
while (i < ids.Count-1 && ids[i + 1] == ids[i] + 1)
{
i++;
isrange = true;
}
if (isrange)
Console.Write("-" + ids[i]);
if (!(i + 1 == ids.Count))
Console.Write(",");
}
There is a linq way to do it (if numbers do not repeat), but I am not sure if it is easier:
int last = -1;
int rank = 0;
IEnumerable<string> grouped = arr
.GroupBy(i =>
{
rank += i - last - 1;
last = i;
return rank;
})
.Select(g => g.Count() == 1 ? g.First().ToString()
: g.First().ToString() + "-" + g.Last().ToString());
It seems rather complicated to me, more resource intensive than necessary and not flexible. Linq is great for many cases, but sometimes it just does not fit well. Simple loop is sometimes the best you can get:
IEnumerable<string> Group(IEnumerable<int> sortedArr)
{
using (var en = sortedArr.GetEnumerator())
{
if (!en.MoveNext())
{
yield break;
}
int first = en.Current;
int last = first;
int count = 1;
while (true)
{
bool end;
if ((end = !en.MoveNext()) || en.Current - last > 1)
{
if (count == 1)
{
yield return first.ToString();
}
//else if (count == 2)
//{
// yield return first.ToString();
// yield return last.ToString();
//}
else
{
yield return first.ToString() + "-" + last.ToString();
}
if (end) { yield break; }
first = en.Current;
count = 1;
}
else
{
++count;
}
last = en.Current;
}
}
}
Benchmarks
Lets measure how (in)efficient the linq actually is here on 10M array:
simple loop: 328MB, 1.2sec
my linq: 790MB, 2.7sec
Fredous linq: 1100MB, 7days (estimated)
the code:
int size = 10000000;
int[] arr = new int[size];
Random rnd = new Random(1);
arr[0] = 0;
for(int i = 1; i < size; ++i)
{
if (rnd.Next(100) < 25)
{
arr[i] = arr[i - 1] + 2;
}
else
{
arr[i] = arr[i - 1] + 1;
}
}
System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();
st.Start();
var res = Group(arr).ToList();
st.Stop();
MessageBox.Show(st.ElapsedMilliseconds.ToString());
MessageBox.Show(res.Sum(s => s.Length).ToString());// to be sure the work is done
Here's what I mean. Suppose S = {1, 4} and N = 5. The ordered arrangements of elements in the set S would be like
{1}, {4}, {1,1}, {1,4}, {4,1}, {4,4}, {1,1,1}, ....
and the ones that sum up to N are
{1,4}, {4, 1}, {1,1,1,1,1}
I want an algorithm that finds those efficiently.
My "brute force" way would be like
static IEnumerable<IEnumerable<int>> OrderedArrangements(IEnumerable<int> nums, int k)
{
var singles = nums.Select(i => new int[] {i} );
var cumulative = singles;
for(int j = 2; j <= k; ++j)
{
var last = cumulative.Where(list => list.Count() == (j - 1));
var next = from x in singles
from y in last
select x.Concat(y);
cumulative = cumulative.Concat(next);
}
return cumulative;
}
and then
int sumToN = OrderedArrangements(new int[] {1, 4}, N)
.Where(x => x.Sum() == N);
but I'm wondering if there's an obvious and more efficient way to do this.
Just in case the above answer isn't clear enough, you could try straight forward recursion e.g.
...
/ \
(1) (4)
/ \ / \
(1)(4) (1)(4)
static void f(int sum, int n, String str, int[] arr){
if (n == sum){
Console.WriteLine(str);
return;
}
if (n > sum) return;
for (int i = 0; i < arr.Length; i++){
f(sum, n + arr[i], str + arr[i].ToString(), arr);
}
}
static void Main(string[] args){
int[] arr = { 1, 4 };
f(5, 0, "", arr);
}
Where sum is N in your question, n is initialized to 0, str is initialized to "" and arr is S in your question.
output:
11111
14
41
This works for me:
static IEnumerable<IEnumerable<int>> OrderedArrangements(IEnumerable<int> nums, int k)
{
return
k <= 0
? new [] { Enumerable.Empty<int>() }
: nums
.SelectMany(
n => OrderedArrangements(nums, k - n),
(n, ns) => new [] { n }.Concat(ns))
.Where(ns => ns.Sum() == k);
}
The result of OrderedArrangements(new [] { 1, 4 }, 5) is:
I ran this performance testing code:
Func<Func<IEnumerable<IEnumerable<int>>>, double> measure = f =>
{
var sw = Stopwatch.StartNew();
var result = f();
sw.Stop();
return sw.Elapsed.TotalMilliseconds;
};
var n = 200;
var a = 0.0;
var b = 0.0;
for (var i = 0; i < n; i++)
{
a += measure(() => OrderedArrangements_A(new [] { 1, 4, 9, 13 }, 50000));
b += measure(() => OrderedArrangements_B(new [] { 1, 4, 9, 13 }, 50000));
}
OrderedArrangements_A is the OP's code and OrderedArrangements_B is mine.
I got an average of 15.6ms for "A" and 0.004ms for "B". My code run about 3,895 times faster for this test.
I have an Array of bytes, representing the RGB values of an image.
How could I group each offset of 3 values (RGB) of this array to apply my tweaks (like removing the repeated colors), maybe using Linq?
["120", "100", "10", "120", "100", "10", "10", "60", "110"]
to
["120", "100", "10", "10", "60", "110"]
You can use Select to add index to your enumeration and later group by index / 3. A bit of post-processing on each of the groups and you should be able to get what you want:
var grouped = source.Select((x,i) => new { x, i })
.GroupBy(x -> x.i / 3)
.Select(g => g.ToList())
.Select(g => new { R = g[0], G = g[1], B = g[2] })
.Distinct();
But that feels quite ugly. If I were you I'd probably write a simple custom LINQ method (an extension method on IEnumerable<int>) to do this more efficiently.
Shorter version that gets the distinct RGB values and their indexes:
string[] a = { "120", "100", "10", "120", "100", "10", "10", "60", "110" };
var l = Enumerable.Range(0, a.Length / 3)
.ToLookup(i => new { R = a[i * 3], G = a[i * 3 + 1], B = a[i * 3 + 2] });
If you don't mind using a loop instead of Linq:
class Program
{
static void Main(string[] args)
{
byte[] array = new byte[] { 120, 100, 10, 120, 100, 10, 10, 60, 110 };
List<byte[]> grouped = new List<byte[]>();
// This loop will populate the list grouped with arrays of 3 bytes each, each representing an value for RGB
for(int i = 0; i + 2 < array.Length; i += 3)
{
byte[] currentColor = new byte[]
{
array[i],
array[i + 1],
array[i + 2]
};
grouped.Add(currentColor);
}
// Here you will remove repeated elements for RGB
// Notice you will have to create the ByteArrayComparer class, you will find the code right under this one
var noRepeatedElements = grouped.Distinct<byte[]>(new ByteArrayComparer());
// Print the non repeated elements for testing purposes
foreach(var rgb in noRepeatedElements)
{
foreach(var value in rgb)
{
Console.Write($"\"{value}\"");
}
}
Console.ReadKey();
}
}
Where ByteArrayComparer is the following class
// This class will compare two distinct byte arrays and check if their elements are the same
public class ByteArrayComparer : IEqualityComparer<byte[]>
{
public bool Equals(byte[] x, byte[] y)
{
int smallerArrayLength = Math.Min(x.Length, y.Length);
bool elementsWithSameValue = true;
for(int i = 0; i < smallerArrayLength; i++)
{
// If there is a single element which is different, we know the arrays are different and can break the loop.
if(x[i] != y[i])
{
elementsWithSameValue = false;
break;
}
}
return elementsWithSameValue;
}
public int GetHashCode(byte[] obj)
{
int hash = 0;
for(int i = 0; i < obj.Length; i++)
{
hash += obj[i].GetHashCode();
}
return hash;
}
}
Note that grouped now is a List of arrays of bytes. Each element in grouped has three elements, representing a single RGB value.
Now you can work with the rgb values as you please.
Using Microsoft's Reactive Framework Team's Interactive Extensions (NuGet "Ix-Main") you can do this:
byte[] array = new byte[]
{
120, 100, 10, 120, 100, 10, 10, 60, 110
};
byte[] results =
array
.Buffer(3)
.Distinct(xs => String.Join(",", xs))
.SelectMany(x => x)
.ToArray();
That will give you { 120, 100, 10, 10, 60, 110 }.
I have two arrays say one is string array and the other is int array
string array has---> "11","11","11","11","12","12" elements and the int array has 1,2,3,4,5,6 respectively.
I want result two arrays containing string array--->"11","12"
and int array---->10,11
If the string array has duplicate elements, the other array containing that respective index value must be added .For example "11" is in 1st,2nd,3rd,4th index So its corresponding value must sum of all those elements in other array.Can it be done?
I have written some code but unable to do it..
static void Main(string[] args)
{
//var newchartValues = ["","","","","","",""];
//var newdates = dates.Split(',');
//string[] newchartarray = newchartValues;
//string[] newdatearray = newdates;
int[] newchartValues = new int[] { 1, 2, 3, 4, 5, 6 };
string[] newdates = new string[] { "11", "11","11","12","12","12" };
int[] intarray = new int[newchartValues.Length];
List<int> resultsumarray = new List<int>();
for (int i = 0; i < newchartValues.Length - 1; i++)
{
intarray[i] = Convert.ToInt32(newchartValues[i]);
}
for (int i = 0; i < newdates.Length; i++)
{
for (int j = 0; j < intarray.Length; j++)
{
if (newdates[i] == newdates[i + 1])
{
intarray[j] += intarray[j + 1];
resultsumarray.Add(intarray[j]);
}
}
resultsumarray.ToArray();
}
}
I don't quite get what you need, but I think I fixed your code, result will contain 10 and 11 in this example:
int[] newchartValues = new int[] { 1, 2, 3, 4, 5, 6 };
string[] newdates = new string[] { "11", "11", "11", "11", "12", "12" };
List<int> result = new List<int>();
if (newdates.Length == 0)
return;
string last = newdates[0];
int cursum = newchartValues[0];
for (var i = 1; i <= newdates.Length; i++)
{
if (i == newdates.Length || newdates[i] != last)
{
result.Add(cursum);
if (i == newdates.Length)
break;
last = newdates[i];
cursum = 0;
}
cursum += newchartValues[i];
}
Here is an approach that should do what you want:
List<int> resultsumarray = newdates
.Select((str, index) => new{ str, index })
.GroupBy(x => x.str)
.Select(xg => xg.Sum(x => newchartValues[x.index]))
.ToList();
Result is a List<int> with two number: 6, 15
Something like this?
int[] newchartValues = new int[] { 1, 2, 3, 4, 5, 6 };
int[] newdates = new int[] { 11, 11,11,12,12,12 };
var pairs = Enumerable.Zip(newdates, newchartValues, (x, y) => new { x, y })
.GroupBy(z => z.x)
.Select(g => new { k = g.Key, s = g.Sum(z => z.y) })
.ToList();
var distinctDates = pairs.Select(p => p.k).ToArray();
var sums = pairs.Select(p => p.s).ToArray();