List with arrays into seperate arrays - c#

I have a constructor which uses a List parameter. Inside that list I have three items which are 3 integer arrays.
public HistogramLogic(List<Tuple<int[], int[], int[]>> separateRGB)
Now I want to initialize three new integer arrays with the arrays inside that list. But when I try this code, the index of my new arrays stay 0.
for (int i = 0; i < histogramXValues.Length; i++)
{
rArray = separateRGB.Select(obj => obj.Item1[i]).ToArray();
gArray = separateRGB.Select(obj => obj.Item2[i]).ToArray();
bArray = separateRGB.Select(obj => obj.Item3[i]).ToArray();
}
Anyone got any suggestions on how to fix this?

Bear in mind, if you have a list of N tuples, you start with 3 x N arrays. Sounds like you want them combined into 3 arrays, each containing all of the elements throughout the list. Which you can do with SelectMany.
rArray = separateRGB.SelectMany(obj => obj.Item1).ToArray();
gArray = separateRGB.SelectMany(obj => obj.Item2).ToArray();
bArray = separateRGB.SelectMany(obj => obj.Item3).ToArray();
Full example:
using System;
using System.Linq;
using System.Collections.Generic;
public class Program
{
static public void HistogramLogic(List<Tuple<int[], int[], int[]>> separateRGB)
{
var rArray = separateRGB.SelectMany(obj => obj.Item1).ToArray();
var gArray = separateRGB.SelectMany(obj => obj.Item2).ToArray();
var bArray = separateRGB.SelectMany(obj => obj.Item3).ToArray();
Console.WriteLine("rArray = {{{0}}}", string.Join(",", rArray));
Console.WriteLine("gArray = {{{0}}}", string.Join(",", gArray));
Console.WriteLine("bArray = {{{0}}}", string.Join(",", bArray));
}
public static void Main()
{
var mockData = new List<Tuple<int[], int[], int[]>>
{
Tuple.Create(new[] {11,12,13}, new[] {14,15,16}, new[] {17,18,19}),
Tuple.Create(new[] {21,22,23}, new[] {24,25,26}, new[] {27,28,29}),
Tuple.Create(new[] {31,32,33}, new[] {34,35,36}, new[] {37,38,39})
};
HistogramLogic(mockData);
}
}
Output:
rArray = {11,12,13,21,22,23,31,32,33}
gArray = {14,15,16,24,25,26,34,35,36}
bArray = {17,18,19,27,28,29,37,38,39}
Click here for code on DotNetFiddle

You can just get the item from touple
rArray = separateRGB.Select(obj => obj.Item1);

Like they sad in comments you did reassign local member in each loop.
You could use something like this.
public HistogramLogic(List<Tuple<int[], int[], int[]>> separateRGB)
{
List<int> rList = new List<int>();
List<int> gList = new List<int>();
List<int> bList = new List<int>();
separateRGB.ForEach((Tuple<int[], int[], int[]> tpl) =>
{
rList.AddRange(tpl.Item1);
gList.AddRange(tpl.Item1);
bList.AddRange(tpl.Item1);
});
rArray = rList.ToArray();
gArray = gList.ToArray();
bArray = bList.ToArray();
}
If you wish to not use temp List object you should know final count of elements in tuple, create local array member to desired size. and fill it. List are more suitable for adding and expanding elements. Maybe you could use one Linq Statement but if I understand goal is to get one int[] array per color. If you take
separateRGB.AsQueryable().Select(m => m.Item1).ToArray();
as a result you get int[][] result instead of simple int array;

Related

Sort a List in which each element contains 2 Values

I have a text file that contains Values in this Format: Time|ID:
180|1
60 |2
120|3
Now I want to sort them by Time. The Output also should be:
60 |2
120|3
180|1
How can I solve this problem? With this:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list.Sort();
for (var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
I got no success ...
3 steps are necessary to do the job:
1) split by the separator
2) convert to int because in a string comparison a 6 comes after a 1 or 10
3) use OrderBy to sort your collection
Here is a linq solution in one line doing all 3 steps:
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
Explanation
x => lambda expression, x denotes a single element in your list
x.Split('|')[0] splits each string and takes only the first part of it (time)
Convert.ToInt32(.. converts the time into a number so that the ordering will be done in the way you desire
list.OrderBy( sorts your collection
EDIT:
Just to understand why you got the result in the first place here is an example of comparison of numbers in string representation using the CompareTo method:
int res = "6".CompareTo("10");
res will have the value of 1 (meaning that 6 is larger than 10 or 6 follows 10)
According to the documentation->remarks:
The CompareTo method was designed primarily for use in sorting or alphabetizing operations.
You should parse each line of the file content and get values as numbers.
string[] lines = File.ReadAllLines("path");
// ID, time
var dict = new Dictionary<int, int>();
// Processing each line of the file content
foreach (var line in lines)
{
string[] splitted = line.Split('|');
int time = Convert.ToInt32(splitted[0]);
int ID = Convert.ToInt32(splitted[1]);
// Key = ID, Value = Time
dict.Add(ID, time);
}
var orderedListByID = dict.OrderBy(x => x.Key).ToList();
var orderedListByTime = dict.OrderBy(x => x.Value).ToList();
Note that I use your ID reference as Key of dictionary assuming that ID should be unique.
Short code version
// Key = ID Value = Time
var orderedListByID = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Key).ToList();
var orderedListByTime = lines.Select(x => x.Split('|')).ToDictionary(x => Convert.ToInt32(x[1]), x => Convert.ToInt32(x[0])).OrderBy(x => x.Value).ToList();
You need to convert them to numbers first. Sorting by string won't give you meaningful results.
times = list.Select(l => l.Split('|')[0]).Select(Int32.Parse);
ids = list.Select(l => l.Split('|')[1]).Select(Int32.Parse);
pairs = times.Zip(ids, (t, id) => new{Time = t, Id = id})
.OrderBy(x => x.Time)
.ToList();
Thank you all, this is my Solution:
var path = #"C:\Users\admin\Desktop\test.txt";
List<string> list = File.ReadAllLines(path).ToList();
list = list.OrderBy(x => Convert.ToInt32(x.Split('|')[0])).ToList();
for(var i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestClass {
public static void main(String[] args) {
List <LineItem> myList = new ArrayList<LineItem>();
myList.add(LineItem.getLineItem(500, 30));
myList.add(LineItem.getLineItem(300, 20));
myList.add(LineItem.getLineItem(900, 100));
System.out.println(myList);
Collections.sort(myList);
System.out.println("list after sort");
System.out.println(myList);
}
}
class LineItem implements Comparable<LineItem>{
int time;
int id ;
#Override
public String toString() {
return ""+ time + "|"+ id + " ";
}
#Override
public int compareTo(LineItem o) {
return this.time-o.time;
}
public static LineItem getLineItem( int time, int id ){
LineItem l = new LineItem();
l.time=time;
l.id=id;
return l;
}
}

How to check if one Element of an array matches or a part of an element of another array in c#?

I have a string array,
string[] numbers= {"one","two","last"}
I have another string array,
string[] variables={"string one","int two","string three"}
How can I get the elements of variables array that has the elements of numbers array.
As a result I have to get "string one","int two" elements.
And I have to remove those elements from variables array and store the remaining elements in a new array. So my new array should be like,
string[] newarray={"string three"}
I have used the following code. But could not get the exact result.
foreach (string variable in variables)
{
if(!variable .Contains(numbers.Any())
{
newarray.add(param);
}
}
Thanks in Advance.
string[] numbers= {"one","two","last"};
string[] variables={"string one","int two","string three"};
string [] variablesWithNumbers = variables.Where(t=>numbers
.Any(u=>t.Contains(u))).ToArray();
string[] newarray = variables.Where(t=>!numbers
.Any(u=>t.Contains(u))).ToArray();
here is the code in csharpad with print
http://csharppad.com/gist/989bbe7e439990bc1f993f46d3e64fe8
You could do for example this:
var result = variables.Where(v=>v.Split(' ').Any(s=>numbers.Contains(s)));
You could try this one:
var newarray = variables.Where(v => !numbers.Any(n => v.IndexOf(n) >= 0).ToArray()
string[] numbers = { "one", "two", "last" };
string[] variables = { "string one", "int two", "string three" };
string[] newarray = variables.Where(v => !numbers.Any(n => v.Contains(n))).ToArray();
variables = variables.Where(v => numbers.Any(n => v.Contains(n))).ToArray();
Linq gives you so many ways to do this... One more path you could follow:
var itemsToDiscard = variables.Zip(numbers, (f, s) => !f.Contains(s)).ToArray();
var newArray = variables.Where((_, i) => itemsToDiscard[i]).ToArray();
Note that your problem is underspecified. Is position meaningful? Should the following find any match or not?
{"zero", "one", "two"}
{"string one","int two","string three"}
My proposed solution doesn't, but its far from clear if you want it to be so. In case order is unimportant, the equivalent code would be:
var itemsToDiscard = numbers.Select(n => variables.All(s => !s.Contains(n))).ToArray();
var resultWithoutPosition = variables.Where((_, i) => itemsToDiscard[i]).ToArray();
Try this
var result = variables.Except(variables.Intersect(numbers, new ContainsComparer())).ToArray();
With this
private class ContainsComparer : IEqualityComparer<string> {
public bool Equals(string x, string y) => y.Contains(x);
public int GetHashCode(string obj) => 0;
}

Randomize two List<string> in C# in same order

I have two string lists en and en1
List<string> en = new List<string>(new string[] { "horse", "cat", "dog", "milk", "honey"});
List<string> en1 = new List<string>(new string[] { "horse1", "cat2", "dog3", "milk4", "honey5" });
And I want radomize their content "Shuffle them" and put this radomized content to new two lists.
But I also want them randomize same way so lists after randomization will still be
en[0] == en1[0]
random content after randomization
{ "cat", "horse", "honey", "milk", "dog"}
{ "cat2", "horse1", "honey5", "milk4", "dog3"}
Two obvious ways:
Take a normal shuffle method, and change it to modify both lists at the same time
Transform the two lists into a single joint list, shuffle that, then split them again
The second sounds cleaner to me. You'd use something like:
var joined = en.Zip(en1, (x, y) => new { x, y }).ToList();
var shuffled = joined.Shuffle(); // Assume this exists
en = shuffled.Select(pair => pair.x).ToList();
en1 = shuffled.Select(pair => pair.y).ToList();
Third obvious way:
Shuffle a list of integers 0, 1, ... , Count-1 and use this list as indexes into the original lists.
Edit
This goes along this lines (for user38...):
List<int> shuffeledIndex = new List<int>();
for(int i = 0; i < en.Count; i++) shuffeledIndex.Add(i);
shuffeledIndex.Shuffle(); // Assume this exists
enshuffeled = en[shuffeledIndex[i]]; // instead en[i]
en1shuffeled = en1[shuffeledIndex[i]];
Adding to Jon Skeet's answer, a nice way to do the shuffling is OrderBy(x => Guid.NewGuid()) so the code would look like
var joined = en.Zip(en1, (x, y) => new { x, y });
var shuffled = joined.OrderBy(x => Guid.NewGuid()).ToList();
en = shuffled.Select(pair => pair.x).ToList();
en1 = shuffled.Select(pair => pair.y).ToList();
Well, I think #Jon Skeet's answer is better than mine (since I only know the basics about C# and probably for some other reasons... :P), however you could shuffle your lists manually with something like this:
for(int i=0;i<en.Count;i++) {
int remainingCount = en.Count - 1;
int exchangeIndex = i + (new Random()).nextInt(0, remainingCount);
swap(en, i, exchangeIndex);
swap(en1, i, exchangeIndex);
}
Of course you would need to write a swap function like swap(List<string> list, int indexA, int indexB):
string tmp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = tmp;
You could first combine the two lists into one using the Zip function:
var zipped = en.Zip(en1, (first, second) => new { first, second }).ToList();
You'll then need a shuffling function to shuffle them. You could use the Fisher–Yates shuffle in an extension method:
public static void FisherYatesShuffle<T>(this IList<T> list)
{
var rnd = new Random();
var x = list.Count;
while (x > 1) {
x--;
var y = rnd.Next(x + 1);
T value = list[y];
list[y] = list[x];
list[x] = value;
}
}
To use the extension method:
var shuffled = zipped.FisherYatesShuffle();
Now you can split them back out again into two separate lists:
en = shuffled.Select(combined => combined.x).ToList();
en1 = shuffled.Select(combined => combined.y).ToList();

How to preserve lists already created before using List.Clear()

When I add data to a List, then append that list to another list, then use List.Clear() on the original list, it empties everything and the lists already appended aren't preserved.
Here's an example of what I'm talking about. Lets say I make 2 lists like this:
List<int> list = new List<int>();
List<List<int>> list2 = new List<List<int>>();
for(int i=0;i<10;i++){
for(int i2=0;i2<10;i2++){
list.Add(i2);
}
list2.Add(list);
list.Clear();
}
When I run list.Clear() it clears out all of the pre-existing lists I already appended to list2
I know a work-around would be to re-arrange the code like this:
List<List<int>> list2 = new List<List<int>>();
for(int i=0;i<10;i++){
List<int> list = new List<int>(); //<- moved inside the for
for(int i2=0;i2<10;i2++){
list.Add(i2);
}
list2.Add(list);
}
But is this normal behavior? Is it possible to preserve the pre-appended lists?
Classes are passed by reference and List<T> is a class. Accordingly, when you clear list it also clears the data passed by reference in list2. See List - do I pass objects or references? for more details.
This should solve the issue shown in your example and create a new copy of list to add to list2 before clearing list:
List<int> list = new List<int>();
List<List<int>> list2 = new List<List<int>>();
for(int i=0;i<10;i++){
for(int i2=0;i2<10;i2++){
list.Add(i2);
}
list2.Add(list.ToList()); //modified to create a new List when adding
list.Clear();
}
See also Jon Skeet's Parameter passing in C# for some broader information on how parameters are passed in C#, especially relating to reference types.
Your work-around isn't a work-around; that's the right way to do it.
List is an object and therefore a reference type. Your list is a list of integer values; your list2 is a list of references to lists of integers.
Calling list.ToList() works because that method returns a new list. From the source:
public static List<TSource> ToList<TSource>(this IEnumerable<TSource> source) {
if (source == null) throw Error.ArgumentNull("source");
return new List<TSource>(source);
}
Here's a bit of code to illustrate the difference between a list of value types and a list of reference types:
void Main()
{
int x = 0, y = 1, z = 2;
var list = new List<int> { x, y, z };
list.ForEach(i => Console.WriteLine(i));
x = y = z = 0;
list.ForEach(i => Console.WriteLine(i));
WorseInt a = new WorseInt { Value = 0 },
b = new WorseInt { Value = 1 },
c = new WorseInt { Value = 2 };
var worseList = new List<WorseInt> { a, b, c };
worseList.ForEach(i => Console.WriteLine(i.Value));
a.Value = b.Value = c.Value = 0;
worseList.ForEach(i => Console.WriteLine(i.Value));
}
public class WorseInt
{
public int Value { get; set; }
}
Output:
0
1
2
0
1
2
0
1
2
0
0
0

Split string, convert ToList<int>() in one line

I have a string that has numbers
string sNumbers = "1,2,3,4,5";
I can split it then convert it to List<int>
sNumbers.Split( new[] { ',' } ).ToList<int>();
How can I convert string array to integer list?
So that I'll be able to convert string[] to IEnumerable
var numbers = sNumbers?.Split(',')?.Select(Int32.Parse)?.ToList();
Recent versions of C# (v6+) allow you to do null checks in-line using the null-conditional operator
You can also do it this way without the need of Linq:
List<int> numbers = new List<int>( Array.ConvertAll(sNumbers.Split(','), int.Parse) );
// Uses Linq
var numbers = Array.ConvertAll(sNumbers.Split(','), int.Parse).ToList();
Better use int.TryParse to avoid exceptions;
var numbers = sNumbers
.Split(',')
.Where(x => int.TryParse(x, out _))
.Select(int.Parse)
.ToList();
Joze's way also need LINQ, ToList() is in System.Linq namespace.
You can convert Array to List without Linq by passing the array to List constructor:
List<int> numbers = new List<int>( Array.ConvertAll(sNumbers.Split(','), int.Parse) );
It is also possible to int array to direct assign value.
like this
int[] numbers = sNumbers.Split(',').Select(Int32.Parse).ToArray();
You can use new C# 6.0 Language Features:
replace delegate (s) => { return Convert.ToInt32(s); } with
corresponding method group Convert.ToInt32
replace redundant constructor call: new Converter<string, int>(Convert.ToInt32) with: Convert.ToInt32
The result will be:
var intList = new List<int>(Array.ConvertAll(sNumbers.Split(','), Convert.ToInt32));
also you can use this Extension method
public static List<int> SplitToIntList(this string list, char separator = ',')
{
return list.Split(separator).Select(Int32.Parse).ToList();
}
usage:
var numberListString = "1, 2, 3, 4";
List<int> numberList = numberListString.SplitToIntList(',');
On Unity3d, int.Parse doesn't work well. So I use like bellow.
List<int> intList = new List<int>( Array.ConvertAll(sNumbers.Split(','),
new Converter<string, int>((s)=>{return Convert.ToInt32(s);}) ) );
Hope this help for Unity3d Users.
My problem was similar but with the inconvenience that sometimes the string contains letters (sometimes empty).
string sNumbers = "1,2,hh,3,4,x,5";
Trying to follow Pcode Xonos Extension Method:
public static List<int> SplitToIntList(this string list, char separator = ',')
{
int result = 0;
return (from s in list.Split(',')
let isint = int.TryParse(s, out result)
let val = result
where isint
select val).ToList();
}
Why stick with just int when we have generics?
What about an extension method like :
public static List<T> Split<T>(this string #this, char separator, out bool AllConverted)
{
List<T> returnVals = new List<T>();
AllConverted = true;
var itens = #this.Split(separator);
foreach (var item in itens)
{
try
{
returnVals.Add((T)Convert.ChangeType(item, typeof(T)));
}
catch { AllConverted = false; }
}
return returnVals;
}
and then
string testString = "1, 2, 3, XP, *, 6";
List<int> splited = testString.Split<int>(',', out _);
You can use this:
List<Int32> sNumberslst = sNumbers.Split(',').ConvertIntoIntList();

Categories