Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apply function to some elements of list

Tags:

c#

list

I have a list of complex objects i.e.

class MyObject 
{
    public bool selected;
    public int id;
    public string name;
}

List<MyObject> theObjects = functionThatSelectsObjectsFromContainer();

And I have a list from another source that just give me int ids that are in the list of objects

List<int> idList = functionThatReturnsListOfIds();

Now for each of the items in the idList I want to set the selected property true. I know I can set up a foreach of one list and then search for the matching items in the other list and set it that way, but I was wondering if there's a different way that might be quicker.

like image 210
Mike Avatar asked May 10 '18 04:05

Mike


People also ask

How do you apply a function to each element of a list?

The best way to apply a function to each element of a list is to use the Python built-in map() function that takes a function and one or more iterables as arguments. It then applies the function to each element of the iterables. An alternate way is to use list comprehension.

How do I call a function from a list in Python?

Create a program to understand Python functions as an object. def MyObject(text): # Pass an argument. # Call the function inside the print() function. # call the function using the str variable.


3 Answers

Conclusion

I did some testing on all my methods below, as well as un-lucky's answer, and the fastest of them all was option 2 below, ie

var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
results.ForEach(o => o.selected = true);

Another way of doing it with Linq, where we iterate around theObjects and check each one to see if its' id exists in idList:

1

var result = theObjects.ForEach(o => o.selected = idList.Contains(o.id) ? true : false);

or using Join and ForEach, where we first extract the matching items using Join and then iterate around those items:

2

var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
results.ForEach(o => o.selected = true);

or, you can use Select with ForEach and FirstOrDefault. This is probably going to be slower than the other 2:

3

theObjects
    .Select(o => o.id)
    .Where(i => idList.Contains(i)).ToList()
    .ForEach(i => 
        theObjects.FirstOrDefault(o => o.id == i).selected = true);

I did some testing on the 3 methods I posted, where we have 10000 MyObjects and 1000 unique ids. I ran each method 1000 times, and then got the mean ElapsedMillliseconds for each.

The results were

1

8.288 ms

2

0.19 ms

3

57.342 ms

one = 0;
two = 0;
three = 0;

for (var i = 0; i <1000; i++) {
    RunTest();
}

oneMean = one / 1000;
twoMean = two / 1000;
threeMean = three / 1000;

where

private void RunTest()
{
    ResetData();
    var stopwatch = Stopwatch.StartNew();
    theObjects.ForEach(o => o.selected = idList.Contains(o.id) ? true : false);
    stopwatch.Stop();
    one += stopwatch.ElapsedMilliseconds;

    ResetData();
    stopwatch = Stopwatch.StartNew();
    var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
    results.ForEach(o => o.selected = true);
    stopwatch.Stop();
    two += stopwatch.ElapsedMilliseconds;

    ResetData();
    stopwatch = Stopwatch.StartNew();
    theObjects
        .Select(o => o.id)
        .Where(i => idList.Contains(i)).ToList()
        .ForEach(i => 
            theObjects.FirstOrDefault(o => o.id == i).selected = true);
    stopwatch.Stop();
    three += stopwatch.ElapsedMilliseconds;
}

private void ResetData()
{
    theObjects = new List<MyObject>();
    idList = new List<int>();
    var rnd = new Random();

    for (var i=0; i<10000; i++) {
        theObjects.Add(new MyObject(){id = i});
    }
    for (var i=0; i<=1000; i++) {

        var r = rnd.Next(0, 1000);
        while (idList.Contains(r)) {
            r = rnd.Next(0, 10000);
        }
        idList.Add(r);
    }
}

I tested un-lucky's answer (most upvotes right now) and it got a mean score of 147.676

foreach(var obj in theObjects.Where(o => idList.Any(i=> i == o.id)))
{
    obj.selected = true;
}
like image 187
Bassie Avatar answered Oct 10 '22 03:10

Bassie


I think you can do something like this, to make that working

foreach(var obj in theObjects.Where(o => idList.Any(i=> i == o.id)))
{
    obj.selected = true;
}
like image 32
sujith karivelil Avatar answered Oct 10 '22 04:10

sujith karivelil


With the help of Linq, you can use Where, ToList and ForEach to achieve your required behaviour -

theObjects.Where(x => idList.Contains(x.id)).ToList().ForEach(y => y.selected = true);
like image 1
Arpit Gupta Avatar answered Oct 10 '22 02:10

Arpit Gupta