So Christmas is coming up and every year my family pulls names from a hat for who should buy present for who, and invariably there are concerns, mostly around spouses buying presents for each other.
Assume the families are like so:
List<List<string>> families = new List<List<string>> ()
{
new List<string>() { "A1", "A2" },
new List<string>() { "B1", "B2" },
new List<string>() { "C1", "C2" },
new List<string>() { "D1", "D2" }
};
People in family A can't buy for the others in their family, likewise for families B, C, D.
We can easily get a family from a given person with:
public static IEnumerable<string> FamilyOf(this List<List<string>> families, string person)
{
return families.Where(family => family.Contains(person)).First();
}
... and we can get all valid pairs with:
var everyone = families.SelectMany(family => family);
var pairs = from giver in everyone
from receiver in everyone
where !families.FamilyOf(giver).Contains(receiver)
select Tuple.Create(giver, receiver);
How can I turn this into the possible collections of permutations of valid givers/receivers that includes everyone? From that I'll just select a random collection.
I wrote a bit of code to solve your problem, but it can sometimes throw an exception, when it gets a bit "unlucky" with picking the pairs. For example if the algorithm pairs A1B2 B1C2 C1A2 -> so only D1 and D2 are left, which causes an exception since it doesn't meet your pairing requirement anymore.
Anyway here is the code, which you might want to expand to prevent it from throwing an exception:
var everyone = families.SelectMany(family => family).ToList();
everyone.Shuffle();
var randPairs = families.SelectMany(family => family)
.Select(p => new {
Giver = p,
Receiver = everyone.PopRandom(x => !p.Contains(x[0]))
});
And the two extension methods for IList:
public static T PopRandom<T>(this IList<T> list, Func<T, bool> predicate)
{
var predicatedList = list.Where(x => predicate(x));
int count = predicatedList.Count();
if (count == 0)
{
throw new Exception();
}
T item = predicatedList.ElementAt(Rand.Next(count));
while (item != null && !predicate(item))
{
item = predicatedList.ElementAt(Rand.Next(list.Count));
}
list.Remove(item);
return item;
}
public static void Shuffle<T>(this IList<T> list)
{
int n = list.Count;
while (n > 1)
{
n--;
int k = Rand.Next(n + 1);
T value = list[k];
list[k] = list[n];
list[n] = value;
}
}
Related
Please note: My question contains pseudo code!
In my army I have foot soldiers.
Every soldier is unique: name, strength etc...
All soldiers have inventory. It can be empty.
Inventory can contain: weapons, shields, other items.
I want to group my footsoldiers by their exact inventory.
Very simple example:
I have a collection of:
Weapons: {"AK-47", "Grenade", "Knife"}
Shields: {"Aegis"}
OtherItems: {"KevlarVest"}
Collection of footsoldiers. (Count = 6)
"Joe" : {"AK-47", "Kevlar Vest"}
"Fred" : {"AK-47"}
"John" : {"AK-47", "Grenade"}
"Rambo" : {"Knife"}
"Foo" : {"AK-47"}
"Bar" : {"KevlarVest"}
These are the resulting groups (count=5) : (already in specific order now)
{"AK-47"}
{"AK-47", "Grenade"}
{"AK-47", "Kevlar Vest"}
{"Knife"}
{"KevlarVest"}
I want to sort the groups by: Weapons, then by shields, then by other items in specific order in which they are declared within their collection.
When I open the inventorygroup {"Knife"} I will find a collection with 1 footsoldier named "Rambo".
Please note: I have made this simplified version, in order not to distract you with the complexity of the data at hand. In my business case I am working with ConditionalActionFlags, that may hold Conditions of a certain type.
Hereby I supply a TestMethod that still fails now.
Can you rewrite the GetSoldierGroupings method so that the TestSoldierGroupings method succeeds ?
public class FootSoldier
{
public string Name { get; set; }
public string[] Inventory { get; set; }
}
public class ArrayComparer<T> : IEqualityComparer<T[]>
{
public bool Equals(T[] x, T[] y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(T[] obj)
{
return obj.Aggregate(string.Empty, (s, i) => s + i.GetHashCode(), s => s.GetHashCode());
}
}
[TestMethod]
public void TestSoldierGroupings()
{
//Arrange
var weapons = new[] { "AK-47", "Grenade", "Knife" };
var shields = new[] { "Aegis" };
var otherItems = new[] { "KevlarVest" };
var footSoldiers = new FootSoldier[]
{
new FootSoldier() { Name="Joe" , Inventory= new string[]{ "AK-47", "Kevlar Vest" } },
new FootSoldier() { Name="Fred" , Inventory= new string[]{ "AK-47" } },
new FootSoldier() { Name="John" , Inventory= new string[]{ "AK-47", "Grenade" } },
new FootSoldier() { Name="Rambo" , Inventory= new string[]{ "Knife" } },
new FootSoldier() { Name="Foo" , Inventory= new string[]{ "AK-47" } },
new FootSoldier() { Name="Bar" , Inventory= new string[]{ "Kevlar Vest" } }
};
//Act
var result = GetSoldierGroupings(footSoldiers, weapons, shields, otherItems);
//Assert
Assert.AreEqual(result.Count, 5);
Assert.AreEqual(result.First().Key, new[] { "AK-47" });
Assert.AreEqual(result.First().Value.Count(), 2);
Assert.AreEqual(result.Last().Key, new[] { "Kevlar Vest" });
Assert.AreEqual(result[new[] { "Knife" }].First().Name, "Rambo");
}
public Dictionary<string[], FootSoldier[]> GetSoldierGroupings(FootSoldier[] footSoldiers,
string[] weapons,
string[] shields,
string[] otherItems)
{
//var result = new Dictionary<string[], FootSoldier[]>();
var result = footSoldiers
.GroupBy(fs => fs.Inventory, new ArrayComparer<string>())
.ToDictionary(x => x.Key, x => x.ToArray());
//TODO: the actual sorting.
return result;
}
You need to group your soldiers by a key of combined items. It can be done using custom comparers.
As for me, I would make it simpler by using String.Join with separator which cannot be met in any weapon, shield etc.
Assuming that a soldiers has a property Items which is an array of strings (like ["AK-47", "Kevlar Vest"]), you can do something like this:
var groups = soldiers
.GroupBy(s => String.Join("~~~", s.Items))
.ToDictionary(g => g.First().Items, g => g.ToArray());
It will result into a Dictionary where key is unique item set, and value is an array of all soldiers having such set.
You may change this code such that it returns IGrouping, array of classes \ structs, Dictionary, whatever else convenient for you.
I would go for a Dictionary or an array of something like SoldiersItemGroup[] with items and soldiers as properties.
Make sure to change such join separator that no weapon can theoretically contain it.
i have a list like
var myList = new List<object> {
new { Day = "Sunday", ID = 15 },
new { Day = "Monday", ID = 20 },
new { Day = "Tuesday", ID = 80 }
};
now i would like to get the previous day of a given ID.
e.g. 80 leads to Monday and Sunday should be the result for 20. The List is ordered by ID!
Ist there a easy way to determine the day-value? ideal would be a linq solution.
var result = myList.LastOrDefault(item => item.ID < 80);
A quick n dirty
way:
myList.Where(c => c.ID < currentId).OrderByDescending(c => c.ID)
.Select(c => c.Day).FirstOrDefault();
myList.TakeWhile(o => o.ID <= givenID).Select(o => o.Day).LastOrDefault();
This is assuming myList is ordered. Otherwise you can just throw in an OrderBy()
(Based on Calculate difference from previous item with LINQ)
for a pure linq way to find the prev value
var value = myList.SelectWithPrevious((prev, cur) => new { prev = prev, cur= cur}).Where((w) => w.cur.ID == 80).First();
Console.WriteLine(value.prev.Day);
Extension
public static IEnumerable<TResult> SelectWithPrevious<TSource, TResult> (this IEnumerable<TSource> source,Func<TSource, TSource, TResult> projection)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
{
yield break;
}
TSource previous = iterator.Current;
while (iterator.MoveNext())
{
yield return projection(previous, iterator.Current);
previous = iterator.Current;
}
}
}
This does give more than you needed. But, it may help in future.
var item = myList.Single(l => l.ID == 80);
var i = myList.IndexOf(item);
var j = (i - 1) % myList.Count;
var prevItem = myList[j];
This will get the item that you're searching for, find it's index in the list, subtract one (the modulo allows you to wrap around the end of the list) and then get the previous item from the list by index.
Edit: The use of the index rather than something like FindLast allows you to not need the ID values to be in ascending order with the days. e.g. Monday could be 100 and Tuesday 15 and it wouldn't make a difference.
Do not use var and object. Try this:
private class ItemList
{
/// <summary>
/// Day
/// </summary>
public string Day { get; set; }
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }
}
And:
List<ItemList> myList = new List<ItemList> {
new ItemList() { Day="S", ID=10 },
new ItemList() { Day="M", ID=20 },
new ItemList() { Day="T", ID=30 },
};
foreach(ItemList key in myList)
{
ItemList result = myList.FindLast(x=>x.ID<key.ID);
if(result!=null)
{
MessageBox.Show("Prev ID: " + result.ID.ToString());
}
}
Is there a fancy LINQ expression that could allow me to do the following in a much more simpler fashion. I have a List<List<double>>, assuming the List are columns in a 2d matrix, I want to swap the list of columns into a list of rows. I have the following obvious solution:
int columns = 5;
var values; // assume initialised as List<List<double>>()
var listOfRows = new List<List<double>>();
for (int i = 0; i < columns ; i++)
{
List<double> newRow = new List<double>();
foreach (List<double> value in values)
{
newRow.Add(value[i]);
}
listOfRows.Add(newRow);
}
You could LINQify the inner loop pretty easily:
vector.AddRange(values.Select(value => value[i]));
Whether or not that improves the readability is left entirely up to you!
Here's a Linq expression that would do what you want - looking at it I'd personally stick with the nested foreach loops though - much easier to read:
var columnList= new List<List<double>>();
columnList.Add(new List<double>() { 1, 2, 3 });
columnList.Add(new List<double>() { 4, 5, 6 });
columnList.Add(new List<double>() { 7, 8, 9 });
columnList.Add(new List<double>() { 10, 11, 12 });
int columnCount = columnList[0].Count;
var rowList = columnList.SelectMany(x => x)
.Select((x, i) => new { V = x, Index = i })
.GroupBy(x => (x.Index + 1) % columnCount)
.Select(g => g.Select( x=> x.V).ToList())
.ToList();
This example also would only work on a matrix with a fixed column count. Basically it's flattening the matrix into a list, then creating the list of rows by grouping by the index of the element in the list modulo the column count.
Edit:
A different approach, much closer to a nested loop and probably similar performance besides the overhead.
int columnCount = columnList[0].Count;
int rowCount = columnList.Count;
var rowList = Enumerable.Range(0, columnCount)
.Select( x => Enumerable.Range(0, rowCount)
.Select(y => columnList[y][x])
.ToList())
.ToList();
var inverted = Enumerable.Range(0, columnCount)
.Select(index => columnList.Select(list => list[index]));
In short, we enumerate the column index from a range and use it to collect the nth element of each list.
Please note that you'll need to check that every list has the same number of columns.
Here's one that works for rectangular (non-ragged) matrices. The C# code here works cut-and-paste into LinqPad, a free, interactive C# programming tool.
I define a postfix operator (that is, an extension method) "Transpose." Use the operator as follows:
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
resulting in something like this:
Original
0.843094345109116
0.981432441613373
0.649207864724662
0.00594645645746331
0.378864820291691
0.336915332515219
Transpose
0.843094345109116
0.649207864724662
0.378864820291691
0.981432441613373
0.00594645645746331
0.336915332515219
The gist of the implementation of this operator is the following
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
var heads = xss.Heads();
var tails = xss.Tails();
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose());
}
Here is the full implementation, with some lines commented out that you can uncomment to monitor how the function works.
void Main()
{
var rand = new Random();
var xss = new [] {
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
new [] {rand.NextDouble(), rand.NextDouble()},
};
xss.Dump("Original");
xss.Transpose().Dump("Transpose");
}
public static class Extensions
{
public static IEnumerable<T> Heads<T>(this IEnumerable<IEnumerable<T>> xss)
{
Debug.Assert(xss != null);
if (xss.Any(xs => xs.IsEmpty()))
return new List<T>();
return xss.Select(xs => xs.First());
}
public static bool IsEmpty<T>(this IEnumerable<T> xs)
{
return xs.Count() == 0;
}
public static IEnumerable<IEnumerable<T>> Tails<T>(this IEnumerable<IEnumerable<T>> xss)
{
return xss.Select(xs => xs.Skip(1));
}
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> xss)
{
// xss.Dump("xss in Transpose");
var heads = xss.Heads()
// .Dump("heads in Transpose")
;
var tails = xss.Tails()
// .Dump("tails in Transpose")
;
var empt = new List<IEnumerable<T>>();
if (heads.IsEmpty())
return empt;
empt.Add(heads);
return empt.Concat(tails.Transpose())
// .Dump("empt")
;
}
}
I am combining some of the answers above, which sometimes had columns and rows inverted form the original answer or from the convention I am used to : row refers to the first index and column to the inner ( second) index. e.g. values[row][column]
public static List<List<T>> Transpose<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int ColumnCount = values[0].Count;
var listByColumns = new List<List<T>>();
foreach (int columnIndex in Enumerable.Range(0, ColumnCount))
{
List<T> valuesByColumn = values.Select(value => value[columnIndex]).ToList();
listByColumns.Add(valuesByColumn);
}
return listByColumns;
}
Actually the word row and column is just our convention of thinking about the data in rows and columns , and sometimes adds more confusion than solving them.
We are actually just swapping the inner index for the outer index. (or flipping the indexes around). So one could also just define the following extension method. . Again I borrowed from above solutions, just put it into something I find readable and fairly compact.
Checks that the inner lists are of equal sized are required.
public static List<List<T>> InsideOutFlip<T>(this List<List<T>> values)
{
if (values.Count == 0 || values[0].Count == 0)
{
return new List<List<T>>();
}
int innerCount = values[0].Count;
var flippedList = new List<List<T>>();
foreach (int innerIndex in Enumerable.Range(0, innerCount))
{
List<T> valuesByOneInner = values.Select(value => value[innerIndex]).ToList();
flippedList.Add(valuesByOneInner);
}
return flippedList;
}
I have an array string[] with values that are mostly convertible to integers.
var values = new [] {"", "1", "2", "a", "3"};
I need to convert values to an array of integers, discarding any items that aren't convertible. So I end up with
var numbers = new [] {1, 2, 3};
What would be the most efficient (quickest and clean code) way to do this?
var numbers = values.Select(
s => {
int n;
if (!int.TryParse((s ?? string.Empty), out n))
{
return (int?)null;
}
return (int?)n;
}
)
.Where(n => n != null)
.Select(n => n.Value)
.ToArray();
Here is my take on this. It uses a separate method but cleanly expresses the conditional parse without using nullable values:
private static IEnumerable<int> ParseInt32s(IEnumerable<string> value)
{
foreach(var value in values)
{
int n;
if(Int32.TryParse(value, out n))
{
yield return n;
}
}
}
Usage:
string[] values;
var parsedValues = ParseInt32s(values).ToArray();
I personally use an extension method that's a little different from what others have posted so far. It specifically uses a custom WeakConverter<TSource, TResult> delegate to avoid the issue in Ron's answer of calling ToString() on every object, while maintaining genericity unlike Jared's answer (though I will concede that sometimes trying to make everything generic is overdoing it -- but in this case, the added effort is really not much for what I consider a significant benefit in terms of reusability).
public delegate bool WeakConverter<TSource, TResult>(TSource source, out TResult result);
public static IEnumerable<TResult> TryConvertAll<TSource, TResult>(this IEnumerable<TSource> source, WeakConverter<TSource, TResult> converter)
{
foreach (TSource original in source)
{
TResult converted;
if (converter(original, out converted))
{
yield return converted;
}
}
}
With this in place, you can convert a string[] to an int[] quite simply and robustly (no double-parsing necessary):
string[] strings = new[] { "1", "2", "abc", "3", "", "123" };
int[] ints = strings.TryConvertAll<string, int>(int.TryParse).ToArray();
foreach (int x in ints)
{
Console.WriteLine(x);
}
Output:
1
2
3
123
This can be done in a single linq statement without having to do the parse twice:
var numbers = values
.Select(c => { int i; return int.TryParse(c, out i) ? i : (int?)null; })
.Where(c => c.HasValue)
.Select(c => c.Value)
.ToArray();
EDIT: Updated to not use try/catch, since StackOverflow users have pointed out that it's slow.
Try this.
var values = new[] { "", "1", "2", "a", "3" };
List<int> numeric_list = new List();
int num_try = 0;
foreach (string string_value in values)
{
if (Int32.TryParse(string_value, out num_try) {
numeric_list.Add(num_try);
}
/* BAD PRACTICE (as noted by other StackOverflow users)
try
{
numeric_list.Add(Convert.ToInt32(string_value));
}
catch (Exception)
{
// Do nothing, since we want to skip.
}
*/
}
return numeric_list.ToArray();
You could try the following
public static IEnumerable<int> Convert(this IEnumerable<string> enumerable) {
Func<string,int?> convertFunc = x => {
int value ;
bool ret = Int32.TryParse(x, out value);
return ret ? (int?)value : null;
};
return enumerable
.Select(convertFunc)
.Where(x => x.HasValue)
.Select(x => x.Value);
}
This can easily then be converted to an array.
var numbers = values.Convert().ToArray();
With some straightforward LINQ:
var numbers = values.Where(x => { int num = 0; return Int32.TryParse(x, out num); })
.Select(num => Int32.Parse(num));
Notably this converts every string twice. Doing this imperatively you'll lose some clarity but gain some speed (as an IEnumerable extension):
public static IEnumerable<int> TryCastToInt<T>(this IEnumerable<T> values)
int num = 0;
foreach (object item in values) {
if (Int32.TryParse(item.ToString(), num)) {
yield return num;
}
}
}
int n;
var values = new[] { "", "1", "2", "a", "3" };
var intsonly = values.Where (v=> Int32.TryParse(v, out n)).Select(x => Int32.Parse(x));
var numbers = values
.Where(x => !String.IsNullOrEmpty(x))
.Where(x => x.All(Char.IsDigit))
.Select(x => Convert.ToInt32(x))
.ToArray();
Let's assume I have a list with objects of type Value. Value has a Name property:
private List<Value> values = new List<Value> {
new Value { Id = 0, Name = "Hello" },
new Value { Id = 1, Name = "World" },
new Value { Id = 2, Name = "World" },
new Value { Id = 3, Name = "Hello" },
new Value { Id = 4, Name = "a" },
new Value { Id = 5, Name = "a" },
};
Now I want to get a list of all "repeating" values (elements where the name property was identical with the name property of the previous element).
In this example I want a list with the two elements "world" and "a" (id = 2 and 5) to be returned.
Is this event possible with linq?
Of course I could so smth. like this:
List<Value> tempValues = new List<Value>();
String lastName = String.Empty();
foreach (var v in values)
{
if (v.Name == lastName) tempValues.Add(v);
lastName = v.Name;
}
but since I want to use this query in a more complex context, maybe there is a "linqish" solution.
There won't be anything built in along those lines, but if you need this frequently you could roll something bespoke but fairly generic:
static IEnumerable<TSource> WhereRepeated<TSource>(
this IEnumerable<TSource> source)
{
return WhereRepeated<TSource,TSource>(source, x => x);
}
static IEnumerable<TSource> WhereRepeated<TSource, TValue>(
this IEnumerable<TSource> source, Func<TSource, TValue> selector)
{
using (var iter = source.GetEnumerator())
{
if (iter.MoveNext())
{
var comparer = EqualityComparer<TValue>.Default;
TValue lastValue = selector(iter.Current);
while (iter.MoveNext())
{
TValue currentValue = selector(iter.Current);
if (comparer.Equals(lastValue, currentValue))
{
yield return iter.Current;
}
lastValue = currentValue;
}
}
}
}
Usage:
foreach (Value value in values.WhereRepeated(x => x.Name))
{
Console.WriteLine(value.Name);
}
You might want to think about what to do with triplets etc - currently everything except the first will be yielded (which matches your description), but that might not be quite right.
You could implement a Zip extension, then Zip your list with .Skip(1) and then Select the rows that match.
This should work and be fairly easy to maintain:
values
.Skip(1)
.Zip(items, (first,second) => first.Name==second.Name?first:null)
.Where(i => i != null);
The slight disadvantage of this method is that you iterate through the list twice.
I think this would work (untested) -- this will give you both the repeated word and it's index. For multiple repeats you could traverse this list and check for consecutive indices.
var query = values.Where( (v,i) => values.Count > i+1 && v == values[i+1] )
.Select( (v,i) => new { Value = v, Index = i } );
Here's another simple approach that should work if the IDs are always sequential as in your sample:
var data = from v2 in values
join v1 in values on v2.Id equals v1.Id + 1
where v1.Name == v2.Name
select v2;
I know this question is ancient but I was just working on the same thing so ....
static class utils
{
public static IEnumerable<T> FindConsecutive<T>(this IEnumerable<T> data, Func<T,T,bool> comparison)
{
return Enumerable.Range(0, data.Count() - 1)
.Select( i => new { a=data.ElementAt(i), b=data.ElementAt(i+1)})
.Where(n => comparison(n.a, n.b)).Select(n => n.a);
}
}
Should work for anything - just provide a function to compare the elements
You could use the GroupBy extension to do this.
Something like this
var dupsNames =
from v in values
group v by v.Name into g
where g.Count > 1 // If a group has only one element, just ignore it
select g.Key;
should work. You can then use the results in a second query:
dupsNames.Select( d => values.Where( v => v.Name == d ) )
This should return a grouping with key=name, values = { elements with name }
Disclaimer: I did not test the above, so I may be way off.