How to use LINQ to compare the values of consecutive "neighbors" inside the list <>?
Take a look at this code:
ColorResult contains Index, Color Name, and Probability
Colors.Add(new ColorResult(1, "Unknown", 5f));
Colors.Add(new ColorResult(2, "Blue", 80f));
Colors.Add(new ColorResult(3, "Blue", 80f));
Colors.Add(new ColorResult(4, "Green", 40f));
Colors.Add(new ColorResult(5, "Blue", 80f));
Colors.Add(new ColorResult(6, "Blue", 80f));
Colors.Add(new ColorResult(7, "Red", 20f));
Colors.Add(new ColorResult(8, "Blue", 80f));
Colors.Add(new ColorResult(9, "Green", 5f));
Using LINQ, how would you do the following:
1) When working sequentially, replace all elements at the beginning of the list <> that have a probability below 60, when the first two elements that follow with a probability above 60 have the same value ("Unknown" becomes "Blue" because # 2 and # 3 are blue and have a probability of 60+)
2) Replace any item with a probability below 60, which is surrounded by four neighbors with the same value ("Green" becomes "Blue" because No. 2, No. 3, No. 5 and No. 6 are blue and have a probability of 60 +)
3) , < > , ( , , ). # 9, № 7 "" 60+ .
, , "" LINQ.
1:
bool partOneCompleted = false;
for (int i = 0; i < Colors.Count; i++)
{
if (Colors[i].ResultConfidence > 60)
{
// This item does not need to be changed
partOneCompleted = true;
}
if (!partOneCompleted)
{
int twoItemsAway = i + 2;
if (twoItemsAway < Colors.Count)
{
if (Colors[twoItemsAway].Name == Colors[twoItemsAway - 1].Name && Colors[twoItemsAway].ResultConfidence > 60 && Colors[twoItemsAway - 1].ResultConfidence > 60)
{
// The next item, and the one after that both have the same value and 60+ confidence
for (int loopBack = i; loopBack >= 0; loopBack--)
{
Colors[loopBack].Name = Colors[twoItemsAway].Name;
}
partOneCompleted = true;
}
}
}
}
- LINQ ?
1:
[Test]
public void Should_convert_leading_low_probability_colors()
{
var colors = new List<ColorResult>
{
new ColorResult(1, "Unknown", 5f),
new ColorResult(2, "Blue", 80f),
new ColorResult(3, "Blue", 80f),
new ColorResult(4, "Green", 40f),
new ColorResult(5, "Blue", 80f),
new ColorResult(6, "Blue", 80f),
new ColorResult(7, "Red", 20f),
new ColorResult(8, "Blue", 80f),
new ColorResult(9, "Green", 5f)
};
ConvertLeadingLowProbabilityColors(colors);
foreach (var colorResult in colors)
{
Console.WriteLine(colorResult.Index + " " + colorResult.Color);
}
colors[0].Color.ShouldBeEqualTo("Blue");
colors[1].Color.ShouldBeEqualTo("Blue");
colors[2].Color.ShouldBeEqualTo("Blue");
colors[3].Color.ShouldBeEqualTo("Green");
colors[4].Color.ShouldBeEqualTo("Blue");
colors[5].Color.ShouldBeEqualTo("Blue");
colors[6].Color.ShouldBeEqualTo("Red");
colors[7].Color.ShouldBeEqualTo("Blue");
colors[8].Color.ShouldBeEqualTo("Green");
}
private void ConvertLeadingLowProbabilityColors(IList<ColorResult> colors)
{
var leadingBelow60 = Enumerable
.Range(0, colors.Count)
.TakeWhile(index => colors[index].Probability < 60)
.ToList();
if (leadingBelow60.Count > 0 && leadingBelow60.Count < colors.Count - 2)
{
int lastIndex = leadingBelow60.Last();
var firstNext = colors[lastIndex + 1];
var secondNext = colors[lastIndex + 2];
if (firstNext.Probability > 60 &&
secondNext.Probability > 60 &&
firstNext.Color == secondNext.Color)
{
leadingBelow60.ForEach(index => colors[index].Color = firstNext.Color);
}
}
}
3, 1:
[Test]
public void Should_convert_trailing_low_probability_colors()
{
var colors = new List<ColorResult>
{
new ColorResult(1, "Unknown", 5f),
new ColorResult(2, "Blue", 80f),
new ColorResult(3, "Blue", 80f),
new ColorResult(4, "Green", 40f),
new ColorResult(5, "Blue", 80f),
new ColorResult(6, "Blue", 80f),
new ColorResult(7, "Red", 20f),
new ColorResult(8, "Blue", 40f),
new ColorResult(9, "Green", 5f)
};
ConvertTrailingLowProbabilityColors(colors);
foreach (var colorResult in colors)
{
Console.WriteLine(colorResult.Index + " " + colorResult.Color);
}
colors[0].Color.ShouldBeEqualTo("Unknown");
colors[1].Color.ShouldBeEqualTo("Blue");
colors[2].Color.ShouldBeEqualTo("Blue");
colors[3].Color.ShouldBeEqualTo("Green");
colors[4].Color.ShouldBeEqualTo("Blue");
colors[5].Color.ShouldBeEqualTo("Blue");
colors[6].Color.ShouldBeEqualTo("Blue");
colors[7].Color.ShouldBeEqualTo("Blue");
colors[8].Color.ShouldBeEqualTo("Blue");
}
:
private void ConvertTrailingLowProbabilityColors(IList<ColorResult> colors)
{
var trailingBelow60 = Enumerable
.Range(0, colors.Count)
.Select(i => colors.Count - 1 - i)
.TakeWhile(index => colors[index].Probability < 60)
.ToList();
if (trailingBelow60.Count > 0 && trailingBelow60.Count < colors.Count - 2)
{
int lastIndex = trailingBelow60.Last();
var firstPrevious = colors[lastIndex - 1];
var secondPrevious = colors[lastIndex - 2];
if (firstPrevious.Probability > 60 &&
secondPrevious.Probability > 60 &&
firstPrevious.Color == secondPrevious.Color)
{
trailingBelow60.ForEach(index => colors[index].Color = firstPrevious.Color);
}
}
}
2. :
[Test]
public void Should_convert_surrounded_low_probability_colors()
{
var colors = new List<ColorResult>
{
new ColorResult(1, "Unknown", 5f),
new ColorResult(2, "Blue", 80f),
new ColorResult(3, "Blue", 80f),
new ColorResult(4, "Green", 40f),
new ColorResult(5, "Blue", 80f),
new ColorResult(6, "Blue", 80f),
new ColorResult(7, "Red", 20f),
new ColorResult(8, "Blue", 80f),
new ColorResult(9, "Green", 5f)
};
ConvertSurroundedLowProbabilityColors(colors);
foreach (var colorResult in colors)
{
Console.WriteLine(colorResult.Index + " " + colorResult.Color);
}
colors[0].Color.ShouldBeEqualTo("Unknown");
colors[1].Color.ShouldBeEqualTo("Blue");
colors[2].Color.ShouldBeEqualTo("Blue");
colors[3].Color.ShouldBeEqualTo("Blue");
colors[4].Color.ShouldBeEqualTo("Blue");
colors[5].Color.ShouldBeEqualTo("Blue");
colors[6].Color.ShouldBeEqualTo("Red");
colors[7].Color.ShouldBeEqualTo("Blue");
colors[8].Color.ShouldBeEqualTo("Green");
}
:
private void ConvertSurroundedLowProbabilityColors(IList<ColorResult> colors)
{
var surrounding4Modification = new Surrounding4ModificationStrategy();
foreach (int index in Enumerable
.Range(0, colors.Count)
.Where(index => surrounding4Modification.IsMatch(colors, index)))
{
surrounding4Modification.Update(colors, index);
}
}
, -:
public class Surrounding4ModificationStrategy
{
public bool IsMatch(IList<ColorResult> input, int index)
{
if (index < 2)
{
return false;
}
if (index >= input.Count - 2)
{
return false;
}
if (input[index].Probability >= 60)
{
return false;
}
var secondPrevious = input[index - 2];
if (secondPrevious.Probability < 60)
{
return false;
}
var firstPrevious = input[index - 1];
if (firstPrevious.Probability < 60)
{
return false;
}
var firstNext = input[index + 1];
if (firstNext.Probability < 60)
{
return false;
}
var secondNext = input[index + 2];
if (secondNext.Probability < 60)
{
return false;
}
if (new[] { secondPrevious.Color, firstPrevious.Color, firstNext.Color, secondNext.Color }.Distinct().Count() > 1)
{
return false;
}
return true;
}
public void Update(IList<ColorResult> input, int index)
{
input[index].Color = input[index + 1].Color;
}
}
, :
[Test]
public void Should_convert_all_low_probability_colors()
{
var colors = new List<ColorResult>
{
new ColorResult(1, "Unknown", 5f),
new ColorResult(2, "Blue", 80f),
new ColorResult(3, "Blue", 80f),
new ColorResult(4, "Green", 40f),
new ColorResult(5, "Blue", 80f),
new ColorResult(6, "Blue", 80f),
new ColorResult(7, "Red", 20f),
new ColorResult(8, "Blue", 80f),
new ColorResult(9, "Green", 5f)
};
ConvertLowProbabilityColors(colors);
foreach (var colorResult in colors)
{
Console.WriteLine(colorResult.Index + " " + colorResult.Color);
}
colors[0].Color.ShouldBeEqualTo("Blue");
colors[1].Color.ShouldBeEqualTo("Blue");
colors[2].Color.ShouldBeEqualTo("Blue");
colors[3].Color.ShouldBeEqualTo("Blue");
colors[4].Color.ShouldBeEqualTo("Blue");
colors[5].Color.ShouldBeEqualTo("Blue");
colors[6].Color.ShouldBeEqualTo("Red");
colors[7].Color.ShouldBeEqualTo("Blue");
colors[8].Color.ShouldBeEqualTo("Green");
}
, , :
public void ConvertLowProbabilityColors(IList<ColorResult> colors)
{
ConvertLeadingLowProbabilityColors(colors);
ConvertSurroundedLowProbabilityColors(colors);
ConvertTrailingLowProbabilityColors(colors);
}
, , , : , < 60 1 3; , ... 1; , 3; .
, . Enumerable.Range, . , .
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace ConsoleApplication1
{
public class ColorResult
{
public int Index;
public string Name;
public float Prob;
public ColorResult(int Index, string Name, float Prob)
{
this.Index = Index;
this.Name = Name;
this.Prob = Prob;
}
public override string ToString()
{
return Index.ToString() + ", " + Name + ", " + Prob.ToString();
}
}
class Program
{
public static void Main()
{
List<ColorResult> Colors = new List<ColorResult>();
Colors.Add(new ColorResult(1, "Unknown", 5f));
Colors.Add(new ColorResult(2, "Blue", 80f));
Colors.Add(new ColorResult(3, "Blue", 80f));
Colors.Add(new ColorResult(4, "Green", 40f));
Colors.Add(new ColorResult(5, "Blue", 80f));
Colors.Add(new ColorResult(6, "Blue", 80f));
Colors.Add(new ColorResult(7, "Red", 20f));
Colors.Add(new ColorResult(8, "Blue", 80f));
Colors.Add(new ColorResult(9, "Green", 5f));
var test1 = from i in Enumerable.Range(0, Colors.Count)
select (i < Colors.Count - 2 &&
(Colors[i].Prob < 60f) &&
(Colors[i + 1].Name == Colors[i + 2].Name) &&
(Colors[i+1].Prob > 60f) &&
(Colors[i+2].Prob > 60f)) ?
new ColorResult(1, Colors[i + 1].Name, Colors[i].Prob) :
Colors[i];
}
}
}
, LINQ, , LINQ. ; , . ( +1) .
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace ConsoleApplication1
{
public class ColorResult
{
public int Index;
public string Name;
public float Prob;
public ColorResult(int Index, string Name, float Prob)
{
this.Index = Index;
this.Name = Name;
this.Prob = Prob;
}
public override string ToString()
{
return Index.ToString() + ", " + Name + ", " + Prob.ToString();
}
}
class Program
{
// Iterate through the list remembering the last two elements
// to implement rule 1
public static IEnumerable<ColorResult> Rule1(IEnumerable<ColorResult> input)
{
ColorResult last2 = null;
ColorResult last1 = null;
foreach (var color in input)
{
if ((color.Prob < 60f)
&& (last1 != null) && (last1.Prob >= 60f)
&& (last2 != null) && (last2.Prob >= 60f)
&& (last2.Name == last1.Name))
{
color.Name = last2.Name;
}
yield return color;
last2 = last1;
last1 = color;
}
}
// Iterate through the list with two element look-ahead
// to implement rule 3
public static IEnumerable<ColorResult> Rule3(IEnumerable<ColorResult> input)
{
ColorResult color = null;
ColorResult ahead1 = null;
foreach (var ahead2 in input)
{
if ((color != null) && (color.Prob < 60f)
&& (ahead1 != null) && (ahead1.Prob >= 60f)
&& (ahead2 != null) && (ahead2.Prob >= 60f)
&& (ahead1.Name == ahead2.Name))
{
color.Name = ahead1.Name;
}
yield return color;
color = ahead1;
ahead1 = ahead2;
}
// Using a null check here as a cheat way to test we've
// actually had two inputs.
// NB Will not preserve trailing nulls in the list;
// you'll need to count inputs if you need that.
if (color != null) yield return color;
if (ahead1 != null) yield return ahead1;
}
public static void Main()
{
List<ColorResult> Colors = new List<ColorResult>();
Colors.Add(new ColorResult(1, "Unknown", 5f));
Colors.Add(new ColorResult(2, "Blue", 80f));
Colors.Add(new ColorResult(3, "Blue", 80f));
Colors.Add(new ColorResult(4, "Green", 40f));
Colors.Add(new ColorResult(5, "Blue", 80f));
Colors.Add(new ColorResult(6, "Blue", 80f));
Colors.Add(new ColorResult(7, "Red", 20f));
Colors.Add(new ColorResult(8, "Blue", 80f));
Colors.Add(new ColorResult(9, "Green", 5f));
var processed = Rule3(Rule1(Colors));
foreach (var color in processed)
{
Console.WriteLine(color);
}
}
}
}
-, , 1 3 2. LINQify 2 :
- , . , .
WithSurroundingNeigbours - , 5 , . : -)
, :
var results = from x in Colors.WithSurroundingNeighbours()
where !x.ItemShouldBeRemoved()
select x[2];
public static class Extensions
{
public static IEnumerable<List<T>> WithSurroundingNeighbours<T>(this IEnumerable<T> input) where T : class
{
var q = new List<T>();
var twoNulls = new T[] {null, null};
foreach (var item in twoNulls.Concat(input).Concat(twoNulls))
{
q.Add(item);
if (q.Count < 5) continue;
yield return q;
q.RemoveAt(0);
}
}
public static bool ItemShouldBeRemoved(this List<ColorResult> items)
{
if (items.Count != 5) throw new ArgumentException("expected list with exactly 5 items");
// TODO: return true when Item3 of the tuple should be removed
// for the first item in the list, Item1 and Item2 are null
// for the last item in the list, Item4 and Item5 are null
return false;
}
}
Here is a complete solution using loops in case anyone is interested:
bool startComplete = false;
for (int i = 0; i < Colors.Count; i++)
{
if (Colors[i].ResultConfidence > 60)
{
// This item does not need to be changed
startComplete = true;
}
if (!startComplete)
{
int twoItemsAway = i + 2;
if (twoItemsAway < Colors.Count)
{
if (Colors[twoItemsAway].Name == Colors[twoItemsAway - 1].Name && Colors[twoItemsAway].ResultConfidence > 60 && Colors[twoItemsAway - 1].ResultConfidence > 60)
{
// The next item, and the one after that both have the same value and 60+ confidence
for (int loopBack = i; loopBack >= 0; loopBack--)
{
Colors[loopBack].Name = Colors[twoItemsAway].Name;
}
startComplete = true;
}
}
}
}
bool endComplete = false;
for (int i = Colors.Count - 1; i >= 0; i--)
{
if (Colors[i].ResultConfidence > 60)
{
// This item does not need to be changed
endComplete = true;
}
if (!endComplete)
{
int twoItemsAway = i - 2;
if (twoItemsAway >= 0)
{
if (Colors[twoItemsAway].Name == Colors[twoItemsAway + 1].Name && Colors[twoItemsAway].ResultConfidence > 60 && Colors[twoItemsAway + 1].ResultConfidence > 60)
{
// The next item, and the one after that both have the same value and 60+ confidence
for (int loopForward = twoItemsAway; loopForward < Colors.Count; loopForward++)
{
Colors[loopForward].Name = Colors[twoItemsAway].Name;
}
endComplete = true;
}
}
}
}
// Fill in the middle values.
for (int i = 2; i < Colors.Count - 2; i++)
{
if (Colors[i].ResultConfidence < 60)
{
int twoLeft = i - 2;
int oneLeft = i - 1;
int oneRight = i + 1;
int twoRight = i + 2;
if (Colors[twoLeft].Name == Colors[oneLeft].Name && Colors[oneLeft].Name == Colors[oneRight].Name && Colors[oneRight].Name == Colors[twoRight].Name
&&
Colors[twoLeft].ResultConfidence > 60 && Colors[oneLeft].ResultConfidence > 60 && Colors[oneRight].ResultConfidence > 60 && Colors[twoRight].ResultConfidence > 60)
{
Colors[i].Name = Colors[oneRight].Name;
}
}
}
You can use linq to work with sequential elements pretty neatly:
var colours = new List<string>(new[]{"red", "green", "blue"});
var added = colours
.Skip(1)
.Zip(colours,
(second, first) => first + second);
foreach (var colour in added)
{
Console.WriteLine(colour);
}
Note that we skip the first element of the sequence and then print the result ourselves. This gives:
Redgreen
greenblue