11

Suppose we have an array with data:

double[] x = new double[N] {x_1, ..., x_N};

And array of size N containing labels corresponding to the elements of x:

int[] ind = new int[N] {i_1, ..., i_N};

What is the fastest way to select all elements from x that have certain label I according to ind?

For example,

x = {3, 2, 6, 2, 5}
ind = {1, 2, 1, 1, 2}
I = ind[0] = 1

Result:

y = {3, 6, 2}

Clearly, it can be easily (but not efficiently and clean) done with loops, but I think there should be the way how to do that using .Where and lambdas..Thanks

EDIT:

The answer provided by MarcinJuraszek is completely correct, thanks. However, I've simplified the question in hope that it would work in my original situation. Could you please take a look what is the problem if we have generic types:

T1[] xn = new T1[N] {x_1, ..., x_N};
T2[] ind = new T2[N] {i_1, ..., i_N};
T2 I = ind[0]

Using the solution provided I get an error "Delegate 'System.Func' does not take 2 arguments":

T1[] y = xn.Where((x, idx) => ind[idx] == I).ToArray();

Thank you very much

2 Answers 2

18

How about that:

var xs = new[] { 3, 2, 6, 2, 5 };
var ind = new[] { 1, 2, 1, 1, 2 };
var I = 1;

var results = xs.Where((x, idx) => ind[idx] == I).ToArray();

It uses second, less popular, Where overload:

Enumerable.Where<TSource>(IEnumerable<TSource>, Func<TSource, Int32, Boolean>)

which has item index available as predicate parameter (called idx in my solution).

Generic version

public static T1[] WhereCorresponding<T1, T2>(T1[] xs, T2[] ind) where T2 : IEquatable<T2>
{
    T2 I = ind[0];
    return xs.Where((x, idx) => ind[idx].Equals(I)).ToArray();
}

Usage

static void Main(string[] args)
{
    var xs = new[] { 3, 2, 6, 2, 5 };
    var ind = new[] { 1, 2, 1, 1, 2 };

    var results = WhereCorresponding(xs, ind);
}

Generic + double version

public static T[] Test<T>(T[] xs, double[] ind)
{
    double I = ind[0];

    return xs.Where((x, idx) => ind[idx] == I).ToArray();
}
Sign up to request clarification or add additional context in comments.

7 Comments

It is very nice, thanks. Could you please look at the edited version of question?
For generics you have to be sure T2 == T2 (or T2.Equals(T2)) is defined - add generic constraint where T2 : IEquatable
Can you advice how should I incorporate this constraint?
Thank you. I'm sorry, but I'm getting an error. If xs is T1 and ind is T2 and results is T2 it says that "The type T2 cannot be used as type parameter... no boxing conversion or type parameter conversion from T2 to System.IEquatable<T2>". I know that I;m doing something wrong, your answer is great. I would appreciate help if you can
Class you're using as T2 does not implement IEquatable<>. Implement it and it's gonna work.
|
5

This is a classic use for Enumerable.Zip, which runs through two enumerables parallel to eachother. Using Zip you can get your results with one pass. The following is completely type-agnostic, though I use ints and strings for illustration:

int[] values = { 3, 2, 6, 2, 5 };
string[] labels = { "A", "B", "A", "A", "B" };
var searchLabel = "A";

var results = labels.Zip(values, (label, value) => new { label, value })
                    .Where(x => x.label == searchLabel)
                    .Select(x => x.value);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.