How to find in an array from right without reversing?

Viewed 1116

I have an array that contains version numbers. They are sorted, but sparse. An example could look like this:

const versions = [
  { version: 1, data: { ... }},
  { version: 3, data: { ... }},
  { version: 17, data: { ... }},
  { version: 95, data: { ... }}
];

Now if I want to access version 17, things are easy:

const data = versions.find(item => item.version === 17);

But what if I want to access version 16, e.g.? From the domain point of view what I would like to get is the highest version that was released before the desired version, in this case version 3.

If we combine both queries, this basically means that I am looking for the last entry in the array that is <= the desired version. Now the problem is: How to do that efficiently.

Things would be really easy if there was a findRight function on arrays, because then I could write:

const data = version.findRight(item => item.version <= 16);

This would return version 3. If we ran the same command with 17, it would return version 17. The only problem here is: There is no findRight function in JavaScript

I can think of two possible workarounds:

  • First, reverse the array, and then use the normal find function. This unfortunately becomes quite slow, as I need to run this query lots of times in a loop for different version arrays.
  • Second, implement a custom findRight function. This would work, but this sounds like solving a problem that has already been solved by someone else.

Before I go to implement my own function – is there a better (i.e. faster) approach that works with pure JavaScript and without npm modules?

8 Answers

You can use Array.findIndex() to get the index of targeted item and then use it to get before release element.

The findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise, it returns -1, indicating no element passed the test

const versions = [{
    version: 1,
    data: {}
  },
  {
    version: 3,
    data: {}
  },
  {
    version: 17,
    data: {}
  },
  {
    version: 95,
    data: {}
  }
];

var index = versions.findIndex(item => item.version === 17);
console.log(versions[index - 1])

You can take last item from the filtered array which is nothing but the previous version:

const versions = [
  { version: 1, data: {  }},
  { version: 3, data: {  }},
  { version: 17, data: {  }},
  { version: 95, data: {  }}
];

const data = versions.filter((item,idx) => item.version < 17);
const previousV = data[data.length -1];
console.log(previousV);

You could create a generator, which returns the elements from the right side.

function* iterRight(array) {
    var i = array.length;
    while (i--) yield array[i];
}

const versions = [{ version: 1, data: {} }, { version: 3, data: {} }, { version: 17, data: {} }, { version: 95, data: {} }];

var item;

for (item of iterRight(versions)) {
    if (item.version <= 16) break;
}

console.log(item);

just use reduceRight()

const versions = [
  { version: 1, data: {}},
  { version: 3, data: {}},
  { version: 17, data: {}},
  { version: 95, data: {}}
];

console.log(versions.reduceRight((a,b) => a.version <= 16 ? a : b ));

If we presume that next is true:

  1. Array is sorted
  2. We will always search for an existing item

then You could use the index of the current item:

e.g.

var data = versions.find((element, index, array) => { return array[index+1].version === 17 });

This way you will get element before the version your are looking for.

You can also use "reduceRight" for this like below

const versions = [
  { version: 1, data: 1},
  { version: 3, data: 3},
  { version: 17, data: 17},
  { version: 95, data: 95}
];

function getNextVersion(v) {
  return versions.slice(0).reduceRight((a , b, i , arr) =>  b.version <= v ? (arr.length = 0, b) : b)
}

console.log(getNextVersion(16))

console.log(getNextVersion(17))

console.log(getNextVersion(18))

console.log(getNextVersion(2))

How about using a filter and get the last element

const versions = [{
    version: 1,
    data: {}
  },
  {
    version: 3,
    data: {}
  },
  {
    version: 17,
    data: {}
  },
  {
    version: 95,
    data: {}
  }
];

function fun(val) {
  let k = versions.filter(item => {
    return item.version < val
  })
  return k[k.length - 1].version

}

console.log(fun(17))

Related