Jon
Jon

Reputation: 40032

Using LINQ to select item in List<T> and add integer to populate new List<T>

Lets say I have List<string> = new List<string>() {"20","26","32"}

I want to create a new List based on the first number in the previous list and it should have the same number of elements in it. I will be adding a certain number to that first number and so on and so on. As an example, using 6 as the number to add I would get 20,26,32. The resulting list will be List. The number 6 is a class wide property.

The issue comes if I have a list of "N","N","32"

I need to produce the same list of 20,26,32 but I have to use the last number to work out the others.

If I had "N","26","N" I would have to use the middle number to work out the others.

The N represents no data in the input list and it will always be this character

In summary, I need to produce a new list with the same number of elements as the input list and it must take the first or next numerical element to produce the resulting list using a specified number to add/subtract values to.

I wondered if LINQ's aggregate function might be able to handle it but got a bit lost using it.

Examples:

"20","26","32" = 20,26,32
"N","26","32" = 20,26,32
"N","N","32" = 20,26,32
"20","26","N" = 20,26,32

Upvotes: 1

Views: 7052

Answers (5)

Gerard ONeill
Gerard ONeill

Reputation: 4092

DevGeezer's answer, but without the cruft. But I still learned alot!

    static List<String> genlist2(List<String> list, int interval)
    {
        if (list == null) return null;

        var vali = list
            .Select((x, i) => x != "N" ? new {val = Convert.ToInt32(x), i } : null)
            .First(x => x != null);

        if (vali == null) return list.ToList();

        return Enumerable.Range(0, list.Count)
            .Select(x => (vali.val - (vali.i - x) * interval).ToString())
            .ToList();
    }

Upvotes: 0

Travis J
Travis J

Reputation: 82267

Seems like a lot of work to do something kinda simple. Here is a non linq approach.

    private List<int> getVals(List<string> input, int modifier)
    {
        if (input == null) return null; if (input.Count < 1) return null;
        foreach (var s in input)
        {
            int i;
            try{i = Convert.ToInt32(s);}
            catch{continue;}
            var returnList = new List<int>(input.Count);
            for (int n = 0; n < input.Count;n++ )returnList[n] = ((n - input.IndexOf(s)) * modifier) + i;
            return returnList;
        }
        return null;
    }

Upvotes: 1

devgeezer
devgeezer

Reputation: 4189

For LINQ purposes, I sometimes resort to writing a parse method that returns an int?as the result so that I can return null when it fails to parse. Here's a complete LINQPad implementation that illustrates this and the positional select (taking an approach otherwise similar to digEmAll's):

void Main()
{
    var n = 6;
    var items = new List<string>
    //    {"20","N", "N"};
    //    {"N", "26", "N"};
        {"N", "N", "32"};

    var first = items
        .Select((v,index) => new { val = Parse(v), index })
        .First(x => x.val.HasValue);

    int start = first.val.Value - n * first.index;

    List<string> values = items
        .Select((x,i) => (i * n + start).ToString())
        .ToList();
}

int? Parse(string strVal)
{
    int ret;
    if (int.TryParse(strVal, out ret))
    {
        return ret;
    }
    return null;
}

Upvotes: 2

digEmAll
digEmAll

Reputation: 57210

What about something like this:

var n = 6;

List<string> strList = new List<string>() {"20","26","32"}; 
// list can also be {null, "26", null} , {null, "N", "32"} , 
//                  {"N", "26", null } etc...

var list = strList.Select(s =>
{
   int v;
   if(string.IsNullOrEmpty(s) || !int.TryParse(s,out v))
      return (int?)null;
   return v;
});

var firstValidVal = list.Select((Num, Index) => new { Num, Index })
                        .FirstOrDefault(x => x.Num.HasValue);
if(firstValidVal == null)
    throw new Exception("No valid number found");

var bases = Enumerable.Range(0, strList.Count).Select(i => i * n);
int startVal = firstValidVal.Num.Value - bases.ElementAt(firstValidVal.Index);

var completeSequence = bases.Select(x => x + startVal);

Upvotes: 3

JaredPar
JaredPar

Reputation: 754585

It sounds like you want a function which will

  • Take a List<int> as input
  • Make the first element of the original list the first element of the new list
  • New list has same number of elements as original
  • Remaining numbers are the first element + a value * position

If so then try the following

static bool TryGetFirstNumber(List<string> list, out number, out index) {
  for (var i = 0; i < list.Count; i++) {
    var cur = list[0];
    if (!String.IsNullOrEmpty(cur) && Int32.TryParse(cur, out number)) {
      index = i;
      return true;
    }
  }
  number = 0;
  index = 0;
  return false;
}

static List<T> TheFunction(List<string> list, int increment) {
  var newList = new List<int>();
  int first;
  int index;
  if (TryGetFirstNumber(list, out first, out index)) {
    first -= index * increment;
  } else {
    first = 0;
  }

  newList.Add(first);
  for (var i = 1; i < list.Length; i++) {
    newList.Add(first + increment);
    increment += increment;
  }

  return newList;
}

Upvotes: 3

Related Questions