I have this method:
public void Foo(double[] values1, double[] values2, int[] indexes)
{
var values1AtIndexes = new List<double>();
var values1NotAtIndexes = new List<double>();
var values2AtIndexes = new List<double>();
var values2NotAtIndexes = new List<double>();
for (int i = 0; i < values1.Length; i++)
{
if (indexes.Contains(i))
{
values1AtIndexes.Add(values1[i]);
values2AtIndexes.Add(values2[i]);
}
else
{
values1NotAtIndexes.Add(values1[i]);
values2NotAtIndexes.Add(values2[i]);
}
}
}
And I was wondering if there is a LINQ-way to this?
It could look like that (not looking at perf).
So I'm not sure that it's shorter, or easier to understand.
values1AtIndexes = values1.Where((m, index) => indexes.Contains(index)).ToList();
values2AtIndexes = values2.Where((m, index) => indexes.Contains(index)).ToList();
//alternative
values1AtIndexes = indexes.Select(m => values1[m]).ToList();
values1AtIndexes = indexes.Select(m => values2[m]).ToList();
//might be wrong if your lists contain duplicated values
values1NotAtIndexes = values1.Except(values1AtIndexes).ToList();
values2NotAtIndexes = values2.Except(values2AtIndexes).ToList();
As you pointed, Except might give you wrong results if you have duplicates.
So if you know you can have duplicates, you should rather do
values1NotAtIndexes = values1.Where((m, index) => !indexes.Contains(index)).ToList();
Related
When i have a list
IList<int> list = new List<int>();
list.Add(100);
list.Add(200);
list.Add(300);
list.Add(400);
list.Add(500);
What is the way to extract a pairs
Example : List elements {100,200,300,400,500}
Expected Pair : { {100,200} ,{200,300} ,{300,400} ,{400,500} }
The most elegant way with LINQ: list.Zip(list.Skip(1), Tuple.Create)
A real-life example: This extension method takes a collection of points (Vector2) and produces a collection of lines (PathSegment) needed to 'join the dots'.
static IEnumerable<PathSegment> JoinTheDots(this IEnumerable<Vector2> dots)
{
var segments = dots.Zip(dots.Skip(1), (a,b) => new PathSegment(a, b));
return segments;
}
This will give you an array of anonymous "pair" objects with A and B properties corresponding to the pair elements.
var pairs = list.Where( (e,i) => i < list.Count - 1 )
.Select( (e,i) => new { A = e, B = list[i+1] } );
You can use a for loop:
var pairs = new List<int[]>();
for(int i = 0; i < list.Length - 1; i++)
pairs.Add(new [] {list[i], list[i + 1]);
You can also use LINQ, but it's uglier:
var pairs = list.Take(list.Count - 1).Select((n, i) => new [] { n, list[i + 1] });
EDIT: You can even do it on a raw IEnumerable, but it's much uglier:
var count = list.Count();
var pairs = list
.SelectMany((n, i) => new [] { new { Index = i - 1, Value = n }, new { Index = i, Value = n } })
.Where(ivp => ivp.Index >= 0 && ivp.Index < count - 1) //We only want one copy of the first and last value
.GroupBy(ivp => ivp.Index, (i, ivps) => ivps.Select(ivp => ivp.Value));
More general would be:
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> values, int count, Func<TSource[], TResult> pairCreator)
{
if (count < 1) throw new ArgumentOutOfRangeException("count");
if (values == null) throw new ArgumentNullException("values");
if (pairCreator == null) throw new ArgumentNullException("pairCreator");
int c = 0;
var data = new TSource[count];
foreach (var item in values)
{
if (c < count)
data[c++] = item;
if (c == count)
{
yield return pairCreator(data);
c = 0;
}
}
}
Following solution uses zip method. Zip originalList and originalList.Skip(1) so that one gets desired result.
var adjacents =
originalList.Zip(originalList.Skip(1),
(a,b) => new {N1 = a, N2 = b});
Using .Windowed() from MoreLINQ:
var source = new[] {100,200,300,400,500};
var result = source.Windowed(2).Select(x => Tuple.Create(x.First(),x.Last()));
Off the top of my head and completely untested:
public static T Pairwise<T>(this IEnumerable<T> list)
{
T last;
bool firstTime = true;
foreach(var item in list)
{
if(!firstTime)
return(Tuple.New(last, item));
else
firstTime = false;
last = item;
}
}
I have been trying to figure out how to randomly order two lists the same eg.
List<string> list = new List<string>();
list.Add("RedHat");
list.Add("BlueHat");
list.Add("YellowHat");
List<image> list2 = new List<image>();
list.Add(Properties.Resources.RedHat);
list.Add(Properties.Resources.BlueHat);
list.Add(Properties.Resources.YellowHat);
now if i wanted to order these so that redhat and the redhat image stay aligned how may i do this?And is there a way to combine these lists and then shuffle using a dictionary or keyvalue pair or something along those lines?
Wrap the two in an object:
class WrapperObject {
public string Name { get; set; }
public object Resource { get; set; }
}
Add them to a list:
var list = new List<WrapperObject>();
list.Add(new WrapperObject() {
Name = "RedHat",
Resource = Properties.Resources.RedHat
});
..randomize:
var rnd = new Random();
list = list.OrderBy(x => rnd.Next(50)).ToList();
Any specific reason why you want them in two lists, you could just create a list of keyvaluepairs like this:
var list = new List<KeyValuePair<string, image>> ();
list.Add(new KeyValuePair<string, image>("RedHat", (Properties.Resources.RedHat)));
list.Add(new KeyValuePair<string, image>("BlueHat", (Properties.Resources.BlueHat)));
list.Add(new KeyValuePair<string, image>("YellowHat", (Properties.Resources.YellowHat)));
You could store the data in a Tuple<,> but if you had more than 2 elements its worth just creating an explicit class to store the data.
Tuple example:
List<Tuple<string, image>> list = new List<Tuple<string, image>>();
list.Add(new Tuple<string,image>("RedHat", Properties.Resources.RedHat));
// etc...
LINQ-fu version:
var rng = new Random();
var res = Enumerable.Zip(list, list2, (e1, e2) => new { e1, e2 })
.OrderBy(x => rng.Next())
.Aggregate(new { list1 = new List<string>(), list2 = new List<image>() },
(lists, next) =>
{
lists.list1.Add(next.e1);
lists.list2.Add(next.e2);
return lists;
});
list = res.list1;
list2 = res.list2;
The following code should do what you want:
var list1 = new List<string>
{
"RedHat",
"BlueHat",
"YellowHat"
};
var list2 = new List<int>
{
1,
2,
3
};
var combined = list1.Zip(list2, (a, b) => new { a, b }).Shuffle(new Random()).ToList();
list1 = combined.Select(i => i.a).ToList();
list2 = combined.Select(i => i.b).ToList();
You'll need the following extension method:
public static class ShuffleExtension
{
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
T[] elements = source.ToArray();
for (int i = elements.Length - 1; i >= 0; i--)
{
int swapIndex = rng.Next(i + 1);
yield return elements[swapIndex];
elements[swapIndex] = elements[i];
}
}
}
First put the corresponding elements together, then apply random order:
var rnd = new Random();
var ordered = list.Zip(list2, Tuple.Create).OrderBy(el => rnd.Next()).ToArray();
You can easily extract back the individual lists, if needed:
var ordered_list = ordered.Select(tuple => tuple.Item1).ToList();
var ordered_list2 = ordered.Select(tuple => tuple.Item2).ToList();
Suppose I have this number list:
List<int> nu = new List<int>();
nu.Add(2);
nu.Add(1);
nu.Add(3);
nu.Add(5);
nu.Add(2);
nu.Add(1);
nu.Add(1);
nu.Add(3);
Keeping the list items in the same order, is it possible to group the items in linq that are sum of 6 so results would be something like this:
2,1,3 - 5 - 2,1,1 - 3
Solving this with LINQ directly would be bothersome, instead you could make an extension method:
// Assumptions:
// (1) All non-negative, or at least you don't mind them in your sum
// (2) Items greater than the sum are returned by their lonesome
static IEnumerable<IEnumerable<int>> GroupBySum(this IEnumerable<int> source,
int sum)
{
var running = 0;
var items = new List<int>();
foreach (var x in source)
{
if (running + x > sum && items.Any())
{
yield return items;
items = new List<int>();
running = 0;
}
running += x;
items.Add(x);
}
if (items.Any()) yield return items;
}
You could do it with Aggregate.
(Side note: Use LinqPad to test/write these types of queries, makes it easy)
Gives these results:
Like this:
class Less7Holder
{
public List<int> g = new List<int>();
public int mySum = 0;
}
void Main()
{
List<int> nu = new List<int>();
nu.Add(2);
nu.Add(1);
nu.Add(3);
nu.Add(5);
nu.Add(2);
nu.Add(1);
nu.Add(1);
nu.Add(3);
var result = nu .Aggregate(
new LinkedList<Less7Holder>(),
(holder,inItem) =>
{
if ((holder.Last == null) || (holder.Last.Value.mySum + inItem >= 7))
{
Less7Holder t = new Less7Holder();
t.g.Add(inItem);
t.mySum = inItem;
holder.AddLast(t);
}
else
{
holder.Last.Value.g.Add(inItem);
holder.Last.Value.mySum += inItem;
}
return holder;
},
(holder) => { return holder.Select((h) => h.g );} );
result.Dump();
}
I am using the below code to read data from a text file row by row. I would like to assign each row into an array. I must be able to find the number or rows/arrays and the number of elements on each one of them.
I would also like to do some manipulations on some or all rows and return their values.
I get the number of rows, but is there a way to to loop something like:
*for ( i=1 to number of rows)
do
mean[i]<-row[i]
done
return mean*
var data = System.IO.File.ReadAllText("Data.txt");
var arrays = new List<float[]>();
var lines = data.Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
{
var lineArray = new List<float>();
foreach (var s in line.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries))
{
lineArray.Add(Convert.ToSingle(s));
}
arrays.Add(lineArray.ToArray());
}
var numberOfRows = lines.Count();
var numberOfValues = arrays.Sum(s => s.Length);
var arrays = new List<float[]>();
//....your filling the arrays
var averages = arrays.Select(floats => floats.Average()).ToArray(); //float[]
var counts = arrays.Select(floats => floats.Count()).ToArray(); //int[]
Not sure I understood the question. Do you mean something like
foreach (string line in File.ReadAllLines("fileName.txt")
{
...
}
Is it ok for you to use Linq? You might need to add using System.Linq; at the top.
float floatTester = 0;
List<float[]> result = File.ReadLines(#"Data.txt")
.Where(l => !string.IsNullOrWhiteSpace(l))
.Select(l => new {Line = l, Fields = l.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries) })
.Select(x => x.Fields
.Where(f => Single.TryParse(f, out floatTester))
.Select(f => floatTester).ToArray())
.ToList();
// now get your totals
int numberOfLinesWithData = result.Count;
int numberOfAllFloats = result.Sum(fa => fa.Length);
Explanation:
File.ReadLines reads the lines of a file (not all at once but straming)
Where returns only elements for which the given predicate is true(f.e. the line must contain more than empty text)
new { creates an anonymous type with the given properties(f.e. the fields separated by comma)
Then i try to parse each field to float
All that can be parsed will be added to an float[] with ToArray()
All together will be added to a List<float[]> with ToList()
Found an efficient way to do this. Thanks for your input everybody!
private void ReadFile()
{
var lines = File.ReadLines("Data.csv");
var numbers = new List<List<double>>();
var separators = new[] { ',', ' ' };
/*System.Threading.Tasks.*/
Parallel.ForEach(lines, line =>
{
var list = new List<double>();
foreach (var s in line.Split(separators, StringSplitOptions.RemoveEmptyEntries))
{
double i;
if (double.TryParse(s, out i))
{
list.Add(i);
}
}
lock (numbers)
{
numbers.Add(list);
}
});
var rowTotal = new double[numbers.Count];
var rowMean = new double[numbers.Count];
var totalInRow = new int[numbers.Count()];
for (var row = 0; row < numbers.Count; row++)
{
var values = numbers[row].ToArray();
rowTotal[row] = values.Sum();
rowMean[row] = rowTotal[row] / values.Length;
totalInRow[row] += values.Length;
}
i have an array below
string stringArray = new stringArray[12];
stringArray[0] = "0,1";
stringArray[1] = "1,3";
stringArray[2] = "1,4";
stringArray[3] = "2,1";
stringArray[4] = "2,4";
stringArray[5] = "3,7";
stringArray[6] = "4,3";
stringArray[7] = "4,2";
stringArray[8] = "4,8";
stringArray[9] = "5,5";
stringArray[10] = "5,6";
stringArray[11] = "6,2";
i need to transform like below
List<List<string>> listStringArray = new List<List<string>>();
listStringArray[["1"],["3","4"],["1","4"],["7"],["3","2","8"],["5","6"],["2"]];
how is that possible?
I think what you actually want is probably this:
var indexGroups = x.Select(s => s.Split(',')).GroupBy(s => s[0], s => s[1]);
This will return the elements as a grouped enumeration.
To return a list of lists, which is what you literally asked for, then try:
var lists = x.Select(s => s.Split(',')).GroupBy(s => s[0], s => s[1])
.Select(g => g.ToList()).ToList();
There's no shorthand like that. You'll have to break into a loop and split each array and add to the list.
Non LINQ version (I must admit its much uglier, but you may have no choice)
var index = new Dictionary<string, List<string>>();
foreach (var str in stringArray) {
string[] split = str.Split(',');
List<string> items;
if (!index.TryGetValue(split[0], out items)) {
items = new List<string>();
index[split[0]] = items;
}
items.Add(split[1]);
}
var transformed = new List<List<string>>();
foreach (List<string> list in index.Values) {
transformed.Add(list);
}