Home > OS >  Extract the desired numbers from the array in the object
Extract the desired numbers from the array in the object

Time:10-26

I have an object with array values , and I want to extract my desired numbers from that with this condition :

First , I should check that if numbers in array are ascending or descending, so if they were one of them, return them , but if they were not check this:

If the numbers in the array are starting from a specific number like 1 or 2 or 1000 and one unit is added each time with a special pattern,like 1, 0, 0, 2, 0, 0, 3, 0, 0 or 1000, 10, 1001, 20, 1002, 30,.. the numbers between them (they also should be consecutive) can be extracted, for example: If this was the array

[1, 300, 400, 2, 500, 650, 3, 800, 1130],

the output should be this: [300, 400, 500, 650, 800, 1130],

Or if this was the array:

[4, 60, 5, 50, 6, 40.3],

the output should be this: [60, 50, 40.3]

So it doesn't matter how much elements are in between 1 and 2 in first array or 4 and 5 in second array, but if there were 3 elements between 1 and 2 , Also 3 elements should be between 3 and 4 , and the numbers between them should be consecutive , (In first array as you can see numbers are ascending and in second array numbers are descending. So here we want consecutive numbers between numbers that added 1 unit every time).

And if none of the conditions were met, just return an empty array for every Object.value that doesn't conditions.

More details :

This is my code and I tried to write the code but I don't know how to find numbers and push consecutive number elements between them and push to new array,I can only check ascending or descending numbers in an array.

const numbers : {
   number1 : [1, 300, 2, 400, 3, 500, 4, 600, 5, 800], 
   number2 : [0, 1.1, 1, 1.2, 2, 1.3, 3, 1.4],
   number3 : [2, 1000, 3, 980, 4, 700, 5,100, 6, 10],
   number4 : [1000, 1001, 1001.3, 1003, 1014],
   number5 : [34, 76, 0, 50, 0.5, 1010, 0.5],
};

//The output I want : 
// {
//    number1 : [300, 400, 500, 600, 800], 
//    number2 : [1.1, 1.2, 1.3, 1.4],
//    number3 : [1000, 980, 700, 100, 10],
//    number4 : [1000, 1001, 1001.3, 1003, 1014],
//    number5 : []
// };

const res = {}
for(var i=0, i < Object.values(numbers).length, i  ){
  el = Object.values(number)[i];
  if (consecutive(el) == true) {
         res[Object.keys(numbers)[i]] = el;
  }else{
        //check and find numbers that added one unit and extract numbers between them.
  }
}

//This function check if numbers are consecutive or not, for consecutive numbers in array returns the last element and for non-consecutive numbers returns false.
const consecutive = (param) => {
  let res = param.reduce((prev , next) => {
    if(prev < next) {
      if(prev == false) {
        return prev
      }
      return next;
    }else{
      return false;
    }
  })
  return (typeof res == 'number') 
}

So How can I find the numbers added one unit every time and delete them and push them to new array?

CodePudding user response:

Seems to me you want an algorithm to define if your array has a paired index per value. Way to go I think is get all odd numbers and see if they are stepped by 1. If so then your array is an index-value pair.

const numbers= {
   number1: [1, 300, 2, 400, 3, 500, 4, 600, 5, 800],
   number2: [0, 1.1, 1, 1.2, 2, 1.3, 3, 1.4],
   number3: [2, 1000, 3, 980, 4, 700, 5,100, 6, 10],
   number4: [1000, 1001, 1001.3, 1003, 1014],
   number5: [34, 76, 0, 50, 0.5, 1010, 0.5],
}

function getEvenNumbers(arr) {
    return arr.filter((n,i) => i % 2 )
}

function getOddNumbers(arr) {
    return arr.filter((n,i) => (i 1) % 2 )
}

function isIndexed(arr) {
    for(let i=1; i < arr.length; i   ) {
        if ( arr[i] !== (arr[i-1]   1) ) return false
    }
    return true
}

function valuesOnly(arr) {
    return isIndexed(getOddNumbers( arr )) ? getEvenNumbers(arr) : arr
}

console.log( 'number1', valuesOnly(numbers.number1) )
console.log( 'number2', valuesOnly(numbers.number2) )
console.log( 'number3', valuesOnly(numbers.number3) )
console.log( 'number4', valuesOnly(numbers.number4) )
console.log( 'number5', valuesOnly(numbers.number5) )

results in:

number1 [300, 400, 500, 600, 800]
number2 [1.1, 1.2, 1.3, 1.4]
number3 [1000, 980, 700, 100, 10]
number4 [1000, 1001, 1001.3, 1003, 1014]
number5 [34, 76, 0, 50, 0.5, 1010, 0.5]

CodePudding user response:

I would suggest a separate function that checks whether an input array is monotone (either all descending, or all ascending).

In case the input is not monotone, it requires a bit of tedious checking, but the idea is to find the position of the starting value 1. If found, then you know the gap size and can verify the rest of this sequence by jumping through the array by that interval, and you can also collect the intermediate values. If the jump exercise confirmed that the "delimiter" values are all incrementing with 1, then you can check that the collected intermediate values are monotone using the function mentioned in the first paragraph.

There is a boundary case where the second value appears multiple times, and more than one possibility exists. This is the case with [1,0,2,2,3,4]. It can be broken up into either of these:

  • [1,[0],2,[2],3,[4]] => [0,2,4], or
  • [1,[0,2],2,[3,4]] => [0,2,3,4]

The solution below will in such case favor the first solution. It is easy to change that behaviour.

Code:

function monotone(numbers) { // Return argument when it is all increasing/decreasing, else []
    if (numbers.length < 2) return numbers;
    let inc = numbers[1] - numbers[0];
    for (let i = 2; i < numbers.length; i  ) {
        if ((numbers[i] - numbers[i-1]) * inc <= 0) return []; 
    }
    return numbers;
}

function extract(numbers) {
    let test = monotone(numbers);
    // We're done when input is all ascending or descending
    if (test.length == numbers.length) return numbers; 
    let start = numbers[0];
    let gap = numbers.indexOf(start   1);
    while (gap > 0) {
        if (numbers.length % gap == 0) {
            collect = [];
            for (let j = 0, expect = start; j < numbers.length && numbers[j] === expect; j  = gap, expect  ) {
                collect.push(...numbers.slice(j   1, j   gap));
            }
            if (collect.length === numbers.length - (numbers.length / gap)) {
                collect = monotone(collect);
                if (collect.length) return collect;
            }
        }
        gap = numbers.indexOf(start   1, gap   1);
    }
    return monotone(numbers);
}

const numbers = {
   number1 : [1,300,2,400,3,500,4,600,5,800], 
   number2 : [0,1.1,1,1.2,2,1.3,3,1.4],
   number3 : [2,1000,3,980,4,700,5,100,6,10],
   number4 : [1000,1001,1001.3,1003,1014],
   number5 : [34 , 76  , 0 , 50 , 0.5 , 1010 , 0.5],
   trincot:  [1,0,2,2,3,4] 
};

const result = Object.fromEntries(
    Object.entries(numbers).map(([key, numbers]) => [key, extract(numbers)])
);

console.log(result);
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

The beneath provided generic solution fulfills every of the OP's requirements.

The approach behind the implemented code is based on two helper functions.

The first one computes the longest position-list of a series of number-values where each value is either incremented or decremented by the value 1, but also is neither a direct antecessor nor a direct successor of another matching number value.

The second helper detects strictly monoton number-series in order to return the correct result in case there is no series of numbers where each number value would separate the other array items in a valid way.

function isStrictlyMonoton(arr) {
  const diff = arr[0] - arr[1];

  const isStrict = ((diff < 0)
    && ((a, b) => a < b)) || ((diff > 0)
    && ((a, b) => a > b)) || false;

  return !!isStrict && arr.every((val, idx, arr) => (idx === 0)
    ? isStrict(val, arr[idx   1])
    : isStrict(arr[idx - 1], val)
  )  
}
function collectSeparatingOneBasedSeriesPositionList(listOfSeries, number, idx, arr) {
  const series = [idx];
  let recentIdx = idx;

  while (
    (idx = arr.indexOf((number = number   1), (idx   2))) > recentIdx
  ) {
    recentIdx = idx;

    series.push(idx);     
  }
  listOfSeries.push(series);

  return listOfSeries;
}

function removeSeparatingOneBasedNumberSeriesValues(arr) {
  if (Array.isArray(arr)) {
    // compute the longest position list of a series of number
    // values which are either incremented or decremented by
    // the value 1, but do separate other array items, thus are
    // not direct antecessors/successors itself.
    const baseIdx = arr.length - 1;
    const longestSeriesPositionList = arr
      .reduce(collectSeparatingOneBasedSeriesPositionList, [])
      .concat(

        // shallow copy in order to not mutate the original reference.
        [...arr] 
          .reverse()
          .reduce(collectSeparatingOneBasedSeriesPositionList, [])
          // re-map/sanitize position values due to the initial `reverse` call.
          .map(positionList => positionList.reverse().map(idx => baseIdx - idx))

      ).sort((a, b) => b.length - a.length)[0];

    if (longestSeriesPositionList.length >= 2) {

      const positionLookup = Object.fromEntries(
        longestSeriesPositionList.map(idx => [idx, true])
      );
      arr = arr.filter((_, idx) => !(idx in positionLookup));

    } else if (!isStrictlyMonoton(arr)) {

      arr = [];
    }
  }
  return arr;
}


Object
  .entries({
    number_1: [1, 300, 2, 400, 3, 500, 4, 600, 5, 800],
    number_2: [0, 1.1, 1.2, 1, 1.3, 1.4, 2],
    number_3: [2, 1000, 3, 980, 4, 700, 5, 100, 6, 10],
    number_4: [1000, 1001, 1001.3, 1003, 1014],
    number_5: [34, 76, 0, 50, 0.5, 1010, 0.5],
    pilchard: [1, 20, 30, 40, 50, 2],
    trincot_A: [1, 1, 2, 2, 3, 4],
    trincot_B: [1, 0, 2, 2, 3, 4],
  })
  .forEach(([key, value]) => {
      console.log(
        `${ key } ... [${ value }] => [${ removeSeparatingOneBasedNumberSeriesValues(value) }]`
      );
      console.log(
        `${ key }/reversed ... [${ [...value].reverse() }] => [${ removeSeparatingOneBasedNumberSeriesValues([...value].reverse()) }]`
      );
  });
.as-console-wrapper { min-height: 100%!important; top: 0; }
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related