shifter
shifter

Reputation: 143

Asynchronously process a list of objects

I have a list of objects, and I need to get one object in that list that best identifies a particular item and add that item in that object.

List<Category> endpoints = new List<Category>();
endpoints = await GetRelatedCategoriesAsync(pp);
float catScore = 0;
Category categoryHit = new Category();

// THIS LOOP SHOULD BE ASYNCHRONOUS
foreach (Category cat in endpoints)
{
    SentenceSimilarity similarity = new SentenceSimilarity();
    string phrase = cat.Keywords.Replace("|", " ");
    float score = similarity.GetScore(phrase, string.IsNullOrEmpty(wholeWord) ? "" : wholeWord);

    if (catScore == 0)
    {
       catScore = score;
       categoryHit = cat;
    }
    else
    {
        if (catScore > score)
        {
            // Do something here
        }
        else
        {
           catScore = score;
           categoryHit = cat;
        }
    }
}

The problem happens when GetRelatedCategoriesAsync returns more than 100 records and the process would take too long, I am having troubles doing it asynchronously so what I did temporarily is that I 'uncategorize' the item if the related categories would reach at least 21 objects.

How do I make this asynchronous, I have also read about SemaphoreSlim but I am not sure if that is what I need to be using.

UPDATE After trying out Yuval Itzchakov suggestion, I am getting the following error:

at System.Linq.Parallel.QueryTaskGroupState.QueryEnd(Boolean userInitiatedDispose)
at System.Linq.Parallel.SpoolingTask.SpoolStopAndGo[TInputOutput,TIgnoreKey](QueryTaskGroupState groupState, PartitionedStream`2 partitions, SynchronousChannel`1[] channels, TaskScheduler taskScheduler)
at System.Linq.Parallel.DefaultMergeHelper`2.System.Linq.Parallel.IMergeHelper<TInputOutput>.Execute()
   at System.Linq.Parallel.MergeExecutor`1.Execute()
   at System.Linq.Parallel.MergeExecutor`1.Execute[TKey](PartitionedStream`2 partitions, Boolean ignoreOutput, ParallelMergeOptions options, TaskScheduler taskScheduler, Boolean isOrdered, CancellationState cancellationState, Int32 queryId)
   at System.Linq.Parallel.PartitionedStreamMerger`1.Receive[TKey](PartitionedStream`2 partitionedStream)
   at System.Linq.Parallel.AssociativeAggregationOperator`3.WrapPartitionedStream[TKey](PartitionedStream`2 inputStream, IPartitionedStreamRecipient`1 recipient, Boolean preferStriping, QuerySettings settings)
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.ChildResultsRecipient.Receive[TKey](PartitionedStream`2 inputStream)
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient)
   at System.Linq.Parallel.UnaryQueryOperator`2.UnaryQueryOperatorResults.GivePartitionedStream(IPartitionedStreamRecipient`1 recipient)
   at System.Linq.Parallel.QueryOperator`1.GetOpenedEnumerator(Nullable`1 mergeOptions, Boolean suppressOrder, Boolean forEffect, QuerySettings querySettings)
   at System.Linq.Parallel.QueryOpeningEnumerator`1.OpenQuery()
   at System.Linq.Parallel.QueryOpeningEnumerator`1.MoveNext()
   at System.Linq.Parallel.AssociativeAggregationOperator`3.Aggregate()
   at System.Linq.AggregationMinMaxHelpers`1.Reduce(IEnumerable`1 source, Int32 sign)
   at System.Linq.AggregationMinMaxHelpers`1.ReduceMax(IEnumerable`1 source)
   at System.Linq.ParallelEnumerable.Max[TSource](ParallelQuery`1 source)
   at System.Linq.ParallelEnumerable.Max[TSource,TResult](ParallelQuery`1 source, Func`2 selector)
   at Scraper.UskoopScraper.<GetCategoryIdAsync>d__80.MoveNext() in d:\projects\\\file.cs:line 1001

Upvotes: 1

Views: 321

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149518

I'm not sure that I understand the exact requirements from you algorithm, but it looks like you're trying to find the maximum out of all categories.

What you can do is define a class which implements IComparable<T> and use PLINQ (Parallel LINQ) by calling AsParallel and use its extension method ParallelEnumerable.Max. It would look similar to this:

public class CategoryResult : IComparable<CategoryResult>
{
    public Category Category { get; set; }
    public int Score { get; set; }

    public int CompareTo(CategoryResult other)
    {
        int comparison = 0;
        if (this.Score == other.Score)
        {
            comparison = 0;
        }
        else if (this.Score > other.Score)
        {
            comparison = 1;
        }
        else if (this.Score < other.Score)
        {
            comparison = -1;
        }

        return comparison;
    }
}

And then you'd call your method like this:

var maxCategoryResult = list.AsParallel().Max(cat => 
{
    SentenceSimilarity similarity = new SentenceSimilarity();
    string phrase = cat.Keywords.Replace("|", " ");
    float score = similarity.GetScore(
                phrase, string.IsNullOrEmpty(wholeWord) ? "" : wholeWord);

    CategoryResult catResult = null;
    if (catScore == 0 || catScore < score)
    {
        catResult = new CategoryResult
        {
            Category = cat,
            Score = score
        }
    }
    else
    {
        // Create different category?
    }

    return catResult;
}

Upvotes: 1

Related Questions