Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I have multiple arrays of objects T -> where T : IComparable<T>.
Each array contains unique elements. An element could be present in multiple arrays but not multiple times in the same array. I need to move all elements that are contains in all arrays to the front of the arrays (in the same order) and get the count of the elements (that are moved so i can have a slice of each array). What would be the most optimal algorithm perf/memory wise?
var a = new[] {4, 5, 6, 7, 8};
var b = new[] {7, 4, 3, 1, 2};
... (up to 8 arrays)
int length = SortAsc(a, b, ...)
// a -> {4, 7, 5, 6, 8}
// b -> {4, 7, 3, 1, 2}
// length = 2
You can use the Intersect method (in System.Linq) to get all the common items. Then you can use Union to join the intersection with the original array. Because we specify intersection.Union(array) instead of array.Union(intersection), the intersection items will appear first in the result. Also, the set operation methods (Union, Intersect, Except) will automatically remove any duplicates:
var a = new[] {4, 5, 6, 7, 8};
var b = new[] {7, 4, 3, 1, 2};
// Common items, ordered by value ascending
var intersection = a.Intersect(b).OrderBy(i => i);
// Union the intersection to put the intersected items first
// (the duplicates are removed automatcially by Union)
a = intersection.Union(a).ToArray();
b = intersection.Union(b).ToArray();
To get the intersection of multiple arrays, it would be handy to add them to a list and then we can use the Aggregate method:
var a = new[] {4, 5, 6, 7, 8};
var b = new[] {7, 4, 3, 1, 2};
var c = new[] {9, 1, 7, 4, 2};
var d = new[] {3, 1, 4, 2, 7};
var e = new[] {3, 7, 4, 1, 2};
var f = new[] {7, 4, 3, 1, 9};
var g = new[] {4, 1, 7, 9, 8};
var h = new[] {3, 2, 6, 7, 4};
var arrays = new List<int[]> {a, b, c, d, e, f, g, h};
var intersection = arrays.Aggregate((accumulation, next) =>
accumulation.Intersect(next).ToArray()).OrderBy(i => i);
Note that this is not the best performing solution, just a simple one to write. :)
Oh, and you can get the count of common items using intersection.Count().
As per I understood, I made an example in a console app:
This is the program.cs
static void Main(string[] args)
{
var a = new int[5] { 4, 5, 6, 7, 8 };
var b = new int[5] { 7, 4, 3, 1, 2 };
var c = new int[5] { 4, 2, 1, 7, 9 };
var d = new int[5] { 1, 2, 6, 4, 5 };
var leng = SortAsc(a, b, c, d);
if (leng.Arrays.Count > 0)
{
Console.WriteLine($"Length: {leng.Arrays.Count}");
foreach (var item in leng.Arrays)
{
for (int i = 0; i < item.Length; i++)
{
Console.Write($"{item[i]}");
}
Console.WriteLine();
}
}
}
This is the method:
public static ArrayKeeper SortAsc(int[] a, int[] b, int[] c, int[] d)
{
//order arrays
a = a.OrderBy(o => o).ToArray();
b = b.OrderBy(o => o).ToArray();
c = c.OrderBy(o => o).ToArray();
d = d.OrderBy(o => o).ToArray();
a = a.OrderByDescending(o => b.Contains(o)).ToArray();
b = b.OrderByDescending(o => a.Contains(o)).ToArray();
c = c.OrderByDescending(o => (a.Contains(o) && b.Contains(o))).ToArray();
d = d.OrderByDescending(o => (a.Contains(o) && b.Contains(o)) && c.Contains(o)).ToArray();
var resp = new ArrayKeeper();
resp.Arrays.Add(b);
resp.Arrays.Add(a);
resp.Arrays.Add(c);
resp.Arrays.Add(d);
return resp;
}
This is a DTO to help transport data;
public class ArrayKeeper {
public ArrayKeeper()
{
Arrays = new List<int[]>();
}
public List<int[]> Arrays { get; set; }
}
This is the result:
$ dotnet run
Length: 4
47123
47568
47129
41256
Of course it's a POC and is just one way to do something like you want using LINQ, Lists and arrays;
Also I think it will need some kind of validation to be more dynamic;
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I need a method to return the select elements in a 1D array where the the position of the elements are stored in another array as follow:
double[] A = new double[11] { 8, 9, 8, 7, 5, 6, 4, 8, 9, 6, 5};
Int32[] C = new Int32[3] { 1, 5, 8};
double[] B = MyMethod(A, C);
It should return:
{9, 6, 9}
I'm lost in Linq's Select, Where, Take :-)
With linq, all you would need to do is Select from the Offset array of int and project those int elements into the indexer of the source array
Here is a generic solution that will work with any type
Given
public static T[] SliceAndDice<T>(T[] source, int[] offsets)
=> offsets.Select(t => source[t]).ToArray();
Usage
var a = new double[11] { 8, 9, 8, 7, 5, 6, 4, 8, 9, 6, 5 };
var b = new int[3] { 1, 5, 8 };
var results = SliceAndDice(a,b);
Console.WriteLine(string.Join(", ", results));
Output
9, 6, 9
If you need the brackets
Console.WriteLine($"{{{string.Join(", ", results)}}}");
Output
{9, 6, 9}
For a completely validated and checked extension method
public static T[] SliceAndDice<T>(this T[] source, int[] offsets)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (offsets == null) throw new ArgumentNullException(nameof(offsets));
var result = new T[offsets.Length];
for (var i = 0; i < offsets.Length; i++)
{
if(offsets[i] >= source.Length)
throw new IndexOutOfRangeException("Index outside the bounds of the source array");
result[i] = source[offsets[i]];
}
return result;
}
This is very similar to the other answer, just stripped down to the bare minimum for an extension method.
static class Program
{
static void Main(string[] args)
{
double[] A = new double[] { 8, 9, 8, 7, 5, 6, 4, 8, 9, 6, 5 };
double[] B = A.Slice(new[] { 1, 5, 8 } );
}
public static T[] Slice<T>(this T[] array, IEnumerable<int> index)
{
return index.Select((i) => array[i]).ToArray();
}
}
You can use overload of LINQ Where extension method which uses index:
var x = A.Where((z, index) => C.Any(j => j == index));
I need to find a way to return the longest match found in number of sets/lists (values returns only once) when the order of items is important.
the list is not cyclic.
A match is a sequence of values that exists in all the lists and maintains the same order of elements in all the lists.
e.g. 1:
List<int> list1 = new List<int> { 1, 2, 3, 4, 7, 9 };
List<int> list2 = new List<int> { 1, 2, 5, 6, 3, 4, 7, 9 };
List<int> list3 = new List<int> { 1, 2, 3, 6, 8, 9 };
List<int> list4 = new List<int> { 1, 2, 5, 6, 8, 9 };
result { 1, 2 }
e.g. 2:
List<int> list1 = new List<int> { 2, 3, 6, 8, 1, 18 };
List<int> list2 = new List<int> { 2, 3, 4, 6, 8, 1, 18, 19, 17, 14 };
List<int> list3 = new List<int> { 2, 5, 6, 8, 1, 18, 16, 13, 14 };
List<int> list4 = new List<int> { 2, 6, 8, 1, 18, 19, 17, 14 };
result { 6, 8, 1, 18 }
The match doesn't have to be found at the beginning or at the end and can be on any part of any list.
I hope that I explained my problem good enough :)
Thanks!
You can build a map from pairs of ints to a count of how many of the lists they appear adjacent in.
Pseudo-code:
For each list L {
For each adjacent pair (x, y) in L {
Counts[x, y] += 1
}
}
Now you can iterate through the first list (or the shortest list), and find the longest run such that each adjacent pair (x, y) in the run with Counts[x, y] showing that the pair appears in every list.
Pseudo-code:
run = []
best_run = []
For x in L[0] {
if len(run) is zero or Counts[run[len(run)-1], x] == number of lists {
run = run + x
} else {
run = [x]
}
if run is longer than best_run {
best_run = run
}
}
This works given the assumption in the question that no integer appears twice in the same list.
This algorithm runs in O(N) time, where N is the sum of the lengths of all the lists.
Here's my approach.
First I need a way to compare lists:
public class ListCompare<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> left, List<T> right)
{
return left.SequenceEqual(right);
}
public int GetHashCode(List<T> list)
{
return list.Aggregate(0, (a, t) => a ^ t.GetHashCode());
}
}
Next a method to produce all subsequences of a source list:
Func<List<int>, IEnumerable<List<int>>> subsequences = xs =>
from s in Enumerable.Range(0, xs.Count)
from t in Enumerable.Range(1, xs.Count - s)
select xs.Skip(s).Take(t).ToList();
Now I can create a list of lists:
var lists = new [] { list1, list2, list3, list4, };
Finally a query that pulls it all together:
var answer =
lists
.Skip(1)
.Aggregate(
subsequences(lists.First()),
(a, l) => a.Intersect(subsequences(l), new ListCompare<int>()))
.OrderByDescending(x => x.Count)
.FirstOrDefault();
Given the sample data provided in the question this produces the expected results.
First generate an ordered combination of int from the shortest list
Compare the lists other than shortest list with the combination. For easy comparison of lists I just convert to string and use string.Contains()
Return immediately if find the match as the items left are next order or the shorter one.
public static List<int> GetLongestMatch(params List<int>[] all)
{
var shortest = all.Where(i => i.Count == all.Select(j => j.Count).Min()).First();
var permutations = (from length in Enumerable.Range(1, shortest.Count)
orderby length descending
from count in Enumerable.Range(1, shortest.Count - length + 1)
select shortest.Skip(count - 1).Take(length).ToList())
.ToList();
Func<List<int>, string> stringfy = (list) => { return string.Join(",", list.Select(i => i.ToString()).ToArray()); };
foreach (var item in permutations)
{
Debug.WriteLine(string.Join(", ", item.Select(i => i.ToString()).ToArray()));
if (all.All(list => stringfy(list).Contains(stringfy(item))))
{
Debug.WriteLine("Matched, skip process and return");
return item;
}
}
return new List<int>();
}
Usage
var result = GetLongestMatch(list1, list2, list3, list4);
Result
2, 3, 6, 8, 1, 18
2, 3, 6, 8, 1
3, 6, 8, 1, 18
2, 3, 6, 8
3, 6, 8, 1
6, 8, 1, 18
Matched, skip process and return
I have a list of objects (i.e. integers) and I want to aggregate sub-lists with LINQ.
For example:
Original list: [ 1, 4, 5, 3, 4, 10, 4, 12 ]
Sub-lists: [ [1,4,5,3], [4,5,3,4], [5,3,4,10], [3,4,10,4], [4,10,4,12] ]
Result (Aggregated List): [ 5, 5, 10, 10, 12 ]
I want to create the maximum of a sub-list for each element containing itself and the following n = 3 elements. Is this possible with LINQ or do I need to create my own aggregation mechanism?
Thanks in advance,Christian
public IEnumerable<IEnumerable<int>> GetSubLists(int[] collection)
{
for(int i = 0; i< collection.Length - 3; i++)
yield return collection.Skip(i).Take(4);
}
GetSubLists(original).Select(l => l.Max());
Or in one line
int[] original = {1, 4, 5, 3, 4, 10, 4, 12 };
int chunkCount = 4;
Enumerable.Range(0, original.Length - chunkCount + 1).Select(i => original.Skip(i).Take(chunkCount))
.Select(l => l.Max());
var result = sublists.Select(sl => sl.Max());
// [5,5,10,10,12]
Creating sub-lists:
List<int> original = new List<int> { 1, 4, 5, 3, 4, 10, 4, 12 };
int sublistSize = 4;
// check if original size is greater than required sublistSize
var sublists = Enumerable.Range(0, original.Count - sublistSize + 1)
.Select(i => original.GetRange(i, sublistSize));
// [[1,4,5,3],[4,5,3,4],[5,3,4,10],[3,4,10,4],[4,10,4,12]]
IEnumerable<int[]> GetLists (int[] list, int size )
{
return Enumerable.Range(0, list.Length - size + 1).Select(x => list.Skip(x).Take(size).ToArray());
}
Sample:
var list = new[] {1, 4, 5, 3, 4, 10, 4, 12};
var max = GetLists(list, 4).Select(x => x.Max()).ToArray();
The Sub-lists intermediate result can be constructed with a "Sliding Window" function.
The desired Result then is the function Max() mapped over the windows with Select().
var originalList = new [] {1, 4, 5, 3, 4, 10, 4, 12};
var sublists = originalList.Window(4); // [ [1,4,5,3], [4,5,3,4], ... ]
var result = sublists.Select(Enumerable.Max); // [ 5, 5, 10, 10, 12 ]
Efficient Window function:
public static IEnumerable<IEnumerable<T>> Window<T>(this IEnumerable<T> source,
int windowSize)
{
if(windowSize < 1) throw new ArgumentException("windowSize must positive", "windowSize");
var q = new Queue<T>(windowSize);
foreach(var e in source)
{
q.Enqueue(e);
if(q.Count < windowSize) continue; // queue not full yet
yield return q;
q.Dequeue();
}
}
List<int> one //1, 3, 4, 6, 7
List<int> second //1, 2, 4, 5
How to get all elements from one list that are present also in second list?
In this case should be: 1, 4
I talk of course about method without foreach. Rather linq query
You can use the Intersect method.
var result = one.Intersect(second);
Example:
void Main()
{
List<int> one = new List<int>() {1, 3, 4, 6, 7};
List<int> second = new List<int>() {1, 2, 4, 5};
foreach(int r in one.Intersect(second))
Console.WriteLine(r);
}
Output:
1
4
static void Main(string[] args)
{
List<int> one = new List<int>() { 1, 3, 4, 6, 7 };
List<int> second = new List<int>() { 1, 2, 4, 5 };
var result = one.Intersect(second);
if (result.Count() > 0)
result.ToList().ForEach(t => Console.WriteLine(t));
else
Console.WriteLine("No elements is common!");
Console.ReadLine();
}
Is there a linq command that will filter out duplicates that appear in a sequence?
Example with '4':
Original { 1 2 3 4 4 4 5 6 7 4 4 4 8 9 4 4 4 }
Filtered { 1 2 3 4 5 6 7 4 8 9 4 }
Thanks.
Not really. I'd write this:
public static IEnumerable<T> RemoveDuplicates(this IEnumerable<T> sequence)
{
bool init = false;
T current = default(T);
foreach (var x in sequence)
{
if (!init || !object.Equals(current, x))
yield return x;
current = x;
init = true;
}
}
Yes there is! One-line code and one loop of the array.
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
var result = source.Where((item, index) => index + 1 == source.Length
|| item != source[index + 1]);
And according to #Hogan's advice, it can be better:
var result = source.Where((item, index) => index == 0
|| item != source[index - 1]);
More readable now i think. It means "choose the first element, and those which isn't equal to the previous one".
Similar to svick's answer, except with side effects to avoid the cons and reverse:
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
List<int> result = new List<int> { source.First() };
source.Aggregate((acc, c) =>
{
if (acc != c)
result.Add(c);
return c;
});
Edit: No longer needs the source.First() as per mquander's concern:
int[] source = new int[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
List<int> result = new List<int>();
result.Add(
source.Aggregate((acc, c) =>
{
if (acc != c)
result.Add(acc);
return c;
})
);
I think I still like Danny's solution the most.
You can use Aggregate() (although I'm not sure whether it's better than the non-LINQ solution):
var ints = new[] { 1, 2, 3, 4, 4, 4, 5, 6, 7, 4, 4, 4, 8, 9, 4, 4, 4 };
var result = ints.Aggregate(
Enumerable.Empty<int>(),
(list, i) =>
list.Any() && list.First() == i
? list
: new[] { i }.Concat(list)).Reverse();
I think it's O(n), but I'm not completely sure.
If you're using .NET 4 then you can do this using the built-in Zip method, although I'd probably prefer to use a custom extension method like the one shown in mquander's answer.
// replace "new int[1]" below with "new T[1]" depending on the type of element
var filtered = original.Zip(new int[1].Concat(original),
(l, r) => new { L = l, R = r })
.Where((x, i) => (i == 0) || !object.Equals(x.L, x.R))
.Select(x => x.L);