slandau
slandau

Reputation: 24062

Function to look through list and determine trend

So I have a list of items. Each item on the list has a property called notional. Now, the list is already sorted. What I need to do is, develop a function that sets the type of list to one of the following:

What would this method look like and what would be the most efficient way to go through the list and figure this out?

Thanks!

Upvotes: 0

Views: 2298

Answers (6)

Roman
Roman

Reputation: 20256

This is basically a Linq implementation of Danish's answer. It'll require (worst case) 3 passes through the list, but because they are so small it won't really matter from a performance point of view. (I wrote it to work on a list of ints so you'll have to modify it easily to work with your types).

var tmp = values
            .Skip(1)
            .Zip( values, (first, second) => first - second )
            .ToList();

var up = tmp.Any( t => t > 0 );
var down = tmp.Any( t => t < 0 );

if( up && down )
    // Rollercoaster
else if( up )
    // Accreting
else if( down )
    // Amortizing
else 
    // Bullet

You could also (ab)use the Aggregate operator and Tuple to do it as one query. However, this will fail if the collection is empty and is a bit weird to use in production code.

var result = values.Skip(1).Aggregate( 
             Tuple.Create<int, bool, bool>( values.First(), false, false ),
             ( last, current ) => {
                 return Tuple.Create( 
                     current, 
                     last.Item2 || (current - last.Item1) > 0,
                     last.Item3 || (current - last.Item1) < 0 );
             });

result will be a tuple that contains:

  1. the last element of the collection (which is of no use)
  2. Item2 will contain a boolean indicating whether any element was bigger than the previous element
  3. Item3 will contain a boolean indicating whether any element was smaller than the previous element

The same switch statement as above can be used to decide which pattern your data follows.

Upvotes: 0

Jon
Jon

Reputation: 437554

This would be a straightforward way to do it:

bool hasGoneUp = false;
bool hasGoneDown = false;
T previous = null; // T is the type of objects in the list; assuming ref type

foreach(var item in list)
{
    if (previous == null) {
        previous = item;
        continue;
    }

    hasGoneUp = hasGoneUp || item.notional > previous.notional;
    hasGoneDown = hasGoneDown || item.notional < previous.notional;

    if(hasGoneUp && hasGoneDown) {
        return Trend.Rollercoaster;
    }

    previous = item;
}

if (!hasGoneUp && !hasGoneDown) {
    return Trend.Bullet;
}

// Exactly one of hasGoneUp and hasGoneDown is true by this point
return hasGoneUp ? Trend.Accreting : Trend.Amortizing;

Upvotes: 2

Viv
Viv

Reputation: 2595

  1. Let trendOut = Bullet
  2. Loop from First Item to Last item

    2.1. If previous notional < next notional

      2.1.a.  If trendOut = Amortizing return RollerCoaster 
      2.1.b.  Else set trendOut = Accreting
    

    2.2. if Previous Notional > next notional

      2.2.a.  If trendOut = Accreting return RollerCoaster
      2.2.b.  Else set trendOut = Amortizing
    
  3. return trendOut.

Upvotes: 1

hectorct
hectorct

Reputation: 3555

        bool OnlyGreaterOrEqual=true;
        bool OnlyLessOrEqual=true;  

        foreach(int i=1;i<itemList.Count;i++){
            if(itemList[i].notional>itemList[i-1].notional){
                OnlyLessOrEqual=false;
            }else if(itemList[i].notional<itemList[i-1].notional){
                OnlyGreaterOrEqual=false;
            }
        }

        if(OnlyGreaterOrEqual && OnlyLessOrEqual){
            return "Bullet";
        }else if(OnlyGreaterOrEqual){
            return "Accreting":
        }else if(OnlyLessOrEqual){
            return "Amortizing";
        }else{
            return "RollerCoast";
        }

Upvotes: 0

Danish
Danish

Reputation: 3798

I usually start of by optimizing for simplicity first and then performance. Hence, I would start by making a second list of N-1 elements, whose {elements} are differences between the {notionals} of the first list.

Hence, for the second list, I would expect the following for the list of your needs

  • Bullet - ALL elements are 0
  • Amortising - ALL elements stay 0 or negative
  • Accreting - ALL elements stay 0 or positive
  • Rollercoaster - Elements oscillate between negative & positive

You can probably optimize it an do it in one pass. Basically, this is a discrete differentiation over your data.

Upvotes: 0

msarchet
msarchet

Reputation: 15242

You could probably do something as simple as this

var changeList = new List<Integer>
for(i = 0; i < yourList.Count() - 1; i++)
{
    changeList.Add(yourList.Item(i + 1) - yourList.Item(i));
}

//Determine if the nature of the list

var positiveChangeCount = changeList.Where(x => x < 0);
var negativeChangeCount = changeList.Where(x => X > 0);

if (positiveChangeCount = yourList.Count)
{
   Accreting;
}
elseif (negativeChangeCount = yourList.Count)
{
   Amortizing;
}
elseif (negativeChangeCount + PositiveChangeCount = 0)
{
  Bullet;
}
else
{
  Rollercoaster;
}

Upvotes: 0

Related Questions