Select given ranges of indexes from source array - c#

I want to create an array with the taken indexes of an array. Let's suppose I have an array like this:
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
And the result I get from my query looks like:
Which means that places 1, 3, 4 (LengthUnit = 2)and 7 are busy (already taken). So, the array now would look like:
| T | 2 | T | T | 5 | 6 | T | 8 | 9 | 10 |
Where, T stands for taken.
How can I create two arrays of integers using the result from query which would look like:
int[] taken = { 1, 3, 4, 7 };
int[] notTaken = { 2, 5, 6, 8, 9, 10 };

Enumerable.Range proofs to be useful in this case:
Dictionary<int, int> startAndLength = new Dictionary<int, int>()
{ { 1, 1 }, { 3, 2 }, { 7, 1 } };
int[] taken = startAndLength
.SelectMany(kvp => Enumerable.Range(kvp.Key, kvp.Value)).ToArray();
int[] notTaken = Enumerable.Range(0, 10).Except(taken).ToArray();
Start creating a set of starts and lengths, then determine the taken items using Enumerable.Range. Then use Enumerable.Range again to determine the items not taken.

Use Enumerable.Range to create the collection of items you want. Then use Except to get the others.
List<int> values = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
List<Tuple<int, int>> ranges = new List<Tuple<int, int>>
{
new Tuple<int, int>(1,1),
new Tuple<int, int>(3,2),
new Tuple<int, int>(7,1),
};
var t = ranges.SelectMany(range => Enumerable.Range(range.Item1, range.Item2)).ToList();
// Here I decide to use Intersect instead of just having t for the case
// where you requested a range that doesn't exist
var taken = values.Intersect(t).ToArray();
var notTaken = values.Except(taken).ToList();
In the case that you want the values and that they aren't sequential like in the example then instead: create a collection of all desired indexes and then get all items of those indexes:
var indexes = ranges.SelectMany(range => Enumerable.Range(range.Item1, range.Item2)).Select(item => item - 1).ToList();
var taken = values.Where((item, index) => indexes.Contains(index)).ToList();
var notTaken = values.Where((item, index) => !indexes.Contains(index)).ToList();

A little Linq will get you a long way:
public class QueryResult
{
public int StartUnit { get; set; }
public int LengthUnit { get; set; }
}
var input = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var queryResult = new QueryResult[]
{
new QueryResult { StartUnit = 1, LengthUnit = 1 },
new QueryResult { StartUnit = 3, LengthUnit = 2 },
new QueryResult { StartUnit = 7, LengthUnit = 1 },
};
var taken = new List<int>();
taken.AddRange(queryResult.SelectMany(q => (input.Skip(q.StartUnit - 1).Take(q.LengthUnit))));
Console.WriteLine("Taken: {0}", string.Join(",", taken));
var notTaken = input.Except(taken);
Console.WriteLine("Not taken: {0}", string.Join(",", notTaken));
Using SelectMany(), Skip() and Take(), you can select the ranges that you wish to include. Using Except() you can then get the items not taken.
Note that this will perform horribly, as it iterates the collections way too many times. It also assumes that the StartUnit actually denotes an (index + 1) in the input collection, not a value.
If you don't actually want to look at the input array, and the values are always contiguous (i.e. no holes in the input), you can use Enumerable.Range() to generate the requested ranges:
taken.AddRange(queryResult.SelectMany(q => Enumerable.Range(q.StartUnit, q.LengthUnit)));
And generate the full range for the Except() to exclude from:
var notTaken = Enumerable.Range(1, 10).Except(taken);
And of course if you want the output to actually be arrays, do a call to ToArray() here and there.

Related

Get all subset sof given array with 'cost' restiction

I have a set of typed elements and price for each type
var array = new []
{
new Elem(0, Types.LowCost),
new Elem(1, Types.MediumCost),
new Elem(2, Types.MediumCost),
new Elem(3, Types.HightCost),
}
And prices: LowCost - 3, MediumCost - 5, HightCost - 9
How would you find all possible unique combinations of elements with restriction "sum of costs for all elements doesn't exceed a restriction"?
For example for MaxCost = 13 I expect
Elem(0) //cost 3
Elem(1) // 5
Elem(2) // 5
Elem(3) // 9
Elem(0), Elem(1) //cost 3+5=8
Elem(0), Elem(2) // 3+5=8
Elem(0), Elem(3) // 3+9=12
Elem(1), Elem(2) // 5+5 = 10
Elem(0), Elem(1), Elem(2) // cost 13
Given a dictionary of costs:
public Dictionary<Types, int> costs = new Dictionary<Types, int>()
{
{ Types.LowCost, 3 },
{ Types.MediumCost, 5 },
{ Types.HightCost, 9 },
};
I can do this:
var query =
from n in Enumerable.Range(0, 1 << array.Length).Skip(1)
let combination = array.Where((x, i) => ((n >> i) & 1) == 1).ToArray()
let cost = combination.Select(x => costs[x.Type]).Sum()
where cost <= 13
select String.Join(", ", combination.Select(x => x.Id));
That gives me:
0
1
0, 1
2
0, 2
1, 2
0, 1, 2
3
0, 3

How to combine lists with respect to precedence

I have a List of strings like this :
List<string> andOrList = new List<string>();
andOrList.Add("AND");
andOrList.Add("OR");
andOrList.Add("AND");
And I have 4 lists to combine :
List<int> list1 = new List<int>(new int[] { 19, 23, 29 });
List<int> list2 = new List<int>(new int[] { 1, 4, 29 });
List<int> list3 = new List<int>(new int[] { 1, 5, 23 });
List<int> list4 = new List<int>(new int[] { 2, 4, 19 });
I want to make a new list from these 4 lists using ANDs and ORs from andOrList. Since AND has higher precedence than OR first I will apply ANDs so I will have these :
var tempList1 = list1.Intersect(list2).ToList();
var tempList2 = list3.Intersect(list4).ToList();
And finally combine these two templists because there is an OR :
var resulList = tempList1.Union(tempList2);
As you can see it's possible to do this by hand when there is defined number of lists and defined number of ANDs and ORs. But I couldn't figure out how to do it programmatically when there are n number of Lists to combine and n-1 number of ANDs and ORs. Can you help me with that? Thanks.
I suggest splitting execution into two stages:
1. Performs all `AND`s
2. Perform all `OR`s
E.g.
a & b & c | d | e & f & g | h == // put the right order
(a & b & c) | (d) | (e & f & g) | (h) == // perform ANDs
a_b_c | d | e_f_g | h == // perform ORs
final result
in your case
{19, 23, 29} & {1, 4, 29} | {1, 5, 23} & {2, 4, 19} == // put the right order
({19, 23, 29} & {1, 4, 29}) | ({1, 5, 23} & {2, 4, 19}) == // perform ANDs
{29} | {} == // perform ORs
{29}
Implementation
private static IEnumerable<T> CombinatorOrAnd<T>(IEnumerable<IEnumerable<T>> sources,
IEnumerable<string> actions) {
List<IEnumerable<T>> orList = new List<IEnumerable<T>>();
// First, do all ANDs
bool isFirst = true;
IEnumerable<T> temp = null;
using (var en = actions.GetEnumerator()) {
foreach (var argument in sources) {
if (isFirst) {
temp = argument;
isFirst = false;
continue;
}
en.MoveNext();
if (en.Current == "AND")
temp = temp.Intersect(argument);
else {
orList.Add(temp);
temp = argument;
}
}
}
orList.Add(temp);
// Finally, perform all ORs
return orList.Aggregate((s, a) => s.Union(a));
}
Test
List<int> list1 = new List<int>(new int[] { 19, 23, 29 });
List<int> list2 = new List<int>(new int[] { 1, 4, 29 });
List<int> list3 = new List<int>(new int[] { 1, 5, 23 });
List<int> list4 = new List<int>(new int[] { 2, 4, 19 });
List<string> andOrList = new List<string>();
andOrList.Add("AND");
andOrList.Add("OR");
andOrList.Add("AND");
var result = CombinatorOrAnd(new List<int>[] { list1, list2, list3, list4}, andOrList);
Console.Write(string.Join(", ", result.OrderBy(item => item)));
Outcome
29
Apologies for the belated answer, but I had this open in the background. The idea was pretty much the same: do the ANDs first, but to do this by mutating (a copy of) the input list.
public static IEnumerable<int> ProcessAndOr(List<string> andOrList, params List<int>[] Input)
{
var lst = new List<IEnumerable<int>>(Input);
for(int i = andOrList.Count -1 ; i >= 0 ; i--)
if(andOrList[i] == "AND")
{
lst[i] = lst[i].Intersect(lst[++i]);
lst.RemoveAt(i--);
}
return lst.SelectMany(l=>l).Distinct();
}
The example could be called with var resultList = ProcessAndOr(andOrList, list1,list2,list3,list4); and produces 29
PS, the reverse order isn't really necessary but is done to be able to use a single variable for iteration.

Sort list based on multiple conditions

I have a list of integer lists, like that:
A -> 10 10 1 1 1
B -> 10 9 9 7 6
...
I would like to sort them based on how many 10s they have, then on how many 9s, 8s, 7s, and so on untile the 1s
So in the example above A should be better than B because even if it has less total points, it has two 10s instead of only 1.
Code should be generic because I don't know how many numbers will be available for each case (sometimes 10, sometimes 5, or even only 3).
I developed something like that:
lists.OrderByDescending(a => a.Where(b => b == 10).Count()).
ThenByDescending(a => a.Where(b => b == 9).Count()).
and so on, but this is not generic...
I hope the question is clear... thank you very much!
You can create query which orders lists by count of 10s, then compose query by adding additional orderings for numbers from 9 to 1:
var query = lists.OrderByDescending(l => l.Count(x => x == 10));
for (int i = 9; i >= 1; i--)
query = query.ThenByDescending(l => l.Count(x => x == i));
For these sample lists:
var lists = new[] {
new[] { 10, 9, 9, 8, 7 },
new[] { 10, 9, 9, 7, 6 },
new[] { 10, 10, 1, 1, 1 }
};
Result will be:
[10, 10, 1, 1, 1]
[10, 9, 9, 8, 7]
[10, 9, 9, 7, 6]
It's simple, but not very efficient. If you need better performance, then consider creating custom comparer. Here is sample with comparer which uses zipped ordered sequences to check if all items in sequences are same, or get first item which is different:
public class CustomComparer : Comparer<IList<int>>
{
public override int Compare(IList<int> x, IList<int> y)
{
var comparisons = x.Zip(y, (a,b) => a.CompareTo(b));
foreach(var comparison in comparisons)
{
if (comparison != 0)
return comparison;
}
return x.Count.CompareTo(y.Count);
}
}
NOTE: If items in lists are not ordered, then you should sort them before zipping:
var comparisons =
x.OrderByDescending(i => i)
.Zip(y.OrderByDescending(i => i), (a,b) => a.CompareTo(b));
It works very simple. Consider two lists:
[10, 9, 9, 8, 7, 5]
[10, 9, 9, 7, 6]
It will create pairs of items in corresponding positions:
{10,10}, {9,9}, {9,9}, {8,7}, {7,6}
Then items in each pair will be compared one by one, until first mismatch will be found:
0, 0, 0, 1 (four comparisons only)
That means first list has more 8s than second one. Usage:
var query = lists.OrderByDescending(i => i, new CustomComparer());
Result is same.
The following comparer
public class Comparer : IComparer<IEnumerable<int>>
{
public int Compare(IEnumerable<int> a, IEnumerable<int> b)
{
var aOrdered = a.OrderByDescending(i => i).Concat(new[] { int.MinValue });
var bOrdered = b.OrderByDescending(i => i).Concat(new[] { int.MinValue });
return a.Zip(b, (i, j) => i.CompareTo(j)).FirstOrDefault(c => c != 0);
}
}
lets you order you lists of lists like so
var result = lists.OrderByDescending(i => i, new Comparer());
without iterating through each list ten times counting individual elements.
This compares the lists and returns conventional comparison result - 1, 0, or -1 is returned depending on whether one value is greater than, equal to, or less than the other.
static int CompareLists(List<int> a, List<int> b)
{
var grpA = a.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
var grpB = b.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
for (int i = 10; i >= 0; i--)
{
int countA = grpA.ContainsKey(i) ? grpA[i] : 0;
int countB = grpB.ContainsKey(i) ? grpB[i] : 0;
int comparison = countA.CompareTo(countB);
if (comparison != 0)
return comparison;
}
return 0;
}
First we convert the lists into dictionary of number->amount of occurences.
Then we iterate through numbers from 10 to 0 and compare the number of occurences. If the result is 0, then we go to another number.
If you have List<List<int>> to sort, just use list.Sort(CompareLists) as in:
List<int> d = new List<int> { 10, 6, 6 };
List<int> b = new List<int> { 10, 9, 9 };
List<int> a = new List<int> { 10, 10, 1, 1, 1 };
List<int> c = new List<int> { 10, 7, 7 };
List<int> e = new List<int> { 9, 3, 7 };
List<int> f = new List<int> { 9, 9, 7 };
List<List<int>> list = new List<List<int>>() { a, b, c, d, e, f };
list.Sort(CompareLists);

Find Same patterns in lists

Assume we have below lists:
List<int> Journey1 = new List<int>() { 1, 2, 3, 4, 5 };
List<int> Journey2 = new List<int>() { 2, 3, 4, 6, 7, 3, 4 };
List<int> Journey3 = new List<int>() { 6, 7, 1 };
List<int> Journey4 = new List<int>() { 3, 1, 4 };
And the patterns are:
2, 3, 4 -> Journey1, Journey2;
6, 7 -> Journey2, Journey3;
1 -> Journey2, Journey3, Journey4;
5 -> Journey1;
3, 4 -> Journey2;
3 -> Journey4;
4 -> Journey4;
We have 5000 lists, and each has around 200 items, so the patterns can have between 1-200 items and can be seen in 1-5000 lists.
Therefore I need very fast way of pattern matching.
Without precomputation and with a naive on-the-fly search:
var matchedJourneys = journeys.Where(x => ContainsPattern(x, mypattern));
bool ContainsPattern(List<int> list, List<int> pattern)
{
for(int i = 0; i < list.Count - (pattern.Count - 1); i++)
{
var match = true;
for(int j = 0; j < pattern.Count; j++)
if(list[i + j] != pattern[j])
{
match = false;
break;
}
if(match) return true;
}
return false;
}
This will execute at max 200 million equals checks for your 'numbers'. But since checks are not expected to be executed for whole patterns, that could be (just a guess) ~5 million equals operations if checking all the lists. That's a few hundred milliseconds.
It all depends on what is 'very fast' for you. If that's too slow, you will need a much much more complicated approach ...
I am not sure what you want as output. I just made a Try.
I suggest that you make a list of lists, instead of declaring individual list variables.
List<List<int>> journeys = new List<List<int>>();
journeys.Add(new List<int>() { 1, 2, 3, 4, 5 });
journeys.Add(new List<int>() { 2, 3, 4, 6, 7, 3, 4 });
journeys.Add(new List<int>() { 6, 7, 1 });
journeys.Add(new List<int>() { 3, 1, 4 });
I assumed that the numbers range from 0 to 255. With this query
var result = Enumerable.Range(0, 256)
.Select(number => new
{
number,
listIndexes = journeys
.Select((list, index) => new { index, list })
.Where(a => a.list.Contains(number))
.Select(a => a.index)
.ToList()
})
.Where(b => b.listIndexes.Count > 0)
.ToList();
and this test loop
foreach (var item in result) {
Console.Write("Number {0} occurs in list # ", item.number);
foreach (var index in item.listIndexes) {
Console.Write("{0} ", index);
}
Console.WriteLine();
}
you will get this result
Number 1 occurs in list # 0 2 3
Number 2 occurs in list # 0 1
Number 3 occurs in list # 0 1 3
Number 4 occurs in list # 0 1 3
Number 5 occurs in list # 0
Number 6 occurs in list # 1 2
Number 7 occurs in list # 1 2
Where the lists are numbered starting at zero.
For brute force approach you can try to use polynomial hash-functions to speed up sub-section matches. Still insane number of comparisons required, but at least match could be almost constant irrespective of sub-sequence length.
In your case there are opportunities to benefit from pattern preprocessing as well as text preprocessing (http://en.wikipedia.org/wiki/String_searching_algorithm).
For instance, constructing a trie for all subsequences in a list will allow to query this list for a given pattern in time proportional to the pattern length.

LINQ for diffing sets

I have the following arrays:
var original= new int[] { 2, 1, 3 };
var target = new int[] { 1, 3, 4 };
enum Operation {Added,Removed}
I would like to execute a LINQ query that would return the following:
{{2,Removed},{4,Added}}
Limitation: I would like LINQ to perform this very efficiently and avoid and O(n^2) style algorithms.
Perhaps a LINQ solution is not the best option in this case.
This will produce a dictionary with the result that you want.
Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
foreach (int value in original) {
difference.Add(value, Operation.Removed);
}
foreach (int value in target) {
if (difference.ContainsKey(value)) {
difference.Remove(value);
} else {
difference.Add(value, Operation.Added);
}
}
To keep the size of the dictionary down, perhaps it's possible to loop the enumerations in parallell. I'll have a look at that...
Edit:
Here it is:
Dictionary<int, Operation> difference = new Dictionary<int,Operation>();
IEnumerator<int> o = ((IEnumerable<int>)original).GetEnumerator();
IEnumerator<int> t = ((IEnumerable<int>)target).GetEnumerator();
bool oActive=true, tActive=true;
while (oActive || tActive) {
if (oActive && (oActive = o.MoveNext())) {
if (difference.ContainsKey(o.Current)) {
difference.Remove(o.Current);
} else {
difference.Add(o.Current, Operation.Removed);
}
}
if (tActive && (tActive = t.MoveNext())) {
if (difference.ContainsKey(t.Current)) {
difference.Remove(t.Current);
} else {
difference.Add(t.Current, Operation.Added);
}
}
}
Edit2:
I did some performance testing. The first version runs 10%-20% faster, both with sorted lists and randomly ordered lists.
I made lists with numbers from 1 to 100000, randomly skipping 10% of the numbers. On my machine the first version of the code matches the lists in about 16 ms.
enum Operation { Added, Removed, }
static void Main(string[] args)
{
var original = new int[] { 2, 1, 3 };
var target = new int[] { 1, 3, 4 };
var result = original.Except(target)
.Select(i => new { Value = i, Operation = Operation.Removed, })
.Concat(
target.Except(original)
.Select(i => new { Value = i, Operation = Operation.Added, })
);
foreach (var item in result)
Console.WriteLine("{0}, {1}", item.Value, item.Operation);
}
I don't think you can do this with LINQ using only a single pass given the stock LINQ extension methods but but might be able to code a custom extension method that will. Your trade off will likely be the loss of deferred execution. It would be interesting to compare the relative performance of both.
You are out of luck. If, as you stated in the comments, the lists are not sorted you can't compute the difference you seek in a single forward pass. Consider:
{ 1, 2, 3, 4, 5, 6, 7, ...
{ 1, 2, 3, 6, 7, 8, 9, ...
At the point where the first difference in encountered (4 vs. 6) it's impossible for you to determine if you are looking at the removal of 4 & 5 (as would be the case if both lists were monotonically increasing, or the insertion of 6, 7, 8, & 9 as would be the case if the lists continued like so:
{ 1, 2, 3, 4, 5, 6, 7, 8, 9,...
{ 1, 2, 3, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9,...
This will achieve the result in a single pass, however I'm not sure of the complexity of the GroupBy operation.
var original= new int[] { 1, 2, 3 };
var target = new int[] { 1, 3, 4 };
var output = original.Select( i => new { I = i, L = "o" } )
.Concat( target.Select( i => new { I = i, L = "t" } ) )
.GroupBy( i => i.I ).Where( i => i.Count() == 1 )
.Select( i => new { I = i.Key, S = (i.ElementAt( 0 ).L == "o" ? Operation.Removed : Operation.Added) } );

Categories

Resources