Ulysses
Ulysses

Reputation: 6015

lodash / js: Filtering values within an object based on regular expressions and getting the highest by comparison

For the following json

[
  {       
    "index": "xyz",
    ...
  },
  {       
    "index": "abc1234",
    ...
  },
  {       
    "index": "xyz",
    ...
  },
  {       
    "index": "abc5678",
    ...
  }
 ...

I want to filter out abc values and xyz values separately.

I tried the following to get values

  var x = _.filter(jsonData, function (o) { 
      return /abc/i.test(o.index); 
  });

and it worked to give the filtered outputs.

Now i want to get the highest of abc values that is if there values abc123, abc444, abc999 then the code should return abc999.

I can loop over again using lodash but could this be done in a single call - within the same one that filters out?

Upvotes: 6

Views: 1620

Answers (7)

Adam Boduch
Adam Boduch

Reputation: 11211

Here's a lodash chaining approach:

_(data)
  .map('index')
  .filter(_.method('match', /abc/))
  .maxBy(_.flow(_.bindKey(/\d+/, 'exec'), _.first, _.toNumber));

The map() and filter() calls get you a list of stings with abc in them. The maxBy() call finds the max, but we have to compose a function to tell it that we want to compare it numerically. The flow() function is really handy for this. Here, we're telling it to execute the regular expression, find the first element of the result, and turn that into a number.

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386680

You could use a single loop with Array#reduce and check the number, if exists.

var data = [{ index: "xyz" }, { index: "abc1234" }, { index: "xyz" }, { index: "abc5678" }],
    getNumber = function (s) { return s.match(/^abc(\d+)/i)[1]; },
    result = data.reduce(function (r, a) {
        return a.index.match(/^abc\d/i) && (!r || getNumber(r.index) < getNumber(a.index)) ? a : r;
    }, undefined);
    
console.log(result);

Upvotes: 1

guest271314
guest271314

Reputation: 1

You can use Array.prototype.reduce(), String.prototype.replace() with RegExp /\D+/ to match and remove characters that are not digits. Check if previous number portion of string is less than current number portion of string

var jsonData = [
  {       
    "index": "xyz",
  },
  {       
    "index": "abc1234",
  },
  {       
    "index": "xyz",
  },
  {       
    "index": "abc5678",
  },
  {       
    "index": "abc1",
  }];

  var x = jsonData.reduce(function (o, prop) { 
           return /abc/i.test(prop.index) 
                  ? !o || +prop.index.replace(/\D+/, "") > +o.replace(/\D+/, "")
                    ? prop.index
                    : o
                  : o
          }, 0);
  
  console.log(x);

Upvotes: 4

FrankCamara
FrankCamara

Reputation: 348

Well in this case you can just use Array prototype sort after you have filtered out the 'abc' to sort them the way you want

var x = _.filter(jsonData, function (o) { 
      return /abc/i.test(o.index); 
  }).sort(function (a, b) {
    if(a.index > b.index) return -1;
    if(a.index < b.index) return 1;
    return 0;
  });

if you do the sorting correct you can get the highest value like

console.log(x[0].index)

Upvotes: 1

Cristian Lupascu
Cristian Lupascu

Reputation: 40566

One way to do it using lodash is by replacing filter with maxBy in your code.

Of course, this has the downside that if no valid elements exist in the collection, it'll arbitrarily return an invalid one. So, after getting the result, an extra validity check is needed.

This is why I have extracted the validation/filter code in a separate function:

var jsonData = [{
  "index": "xyz",
}, {
  "index": "abc1234",
}, {
  "index": "xyz",
}, {
  "index": "abc5678",
}];

var isValid = function(o) {
  return /abc/i.test(o.index);
};

var highest = _.maxBy(jsonData, isValid);

if (isValid(highest)) {
  console.log('The max value is: ' + highest.index);
} else {
  console.log('No valid value found!');
}
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>

And here's how it works if there are no valid elements:

var jsonDataWithoutValidValues = [{
  "index": "xyz",
}, {
  "index": "xyz",
}];

var isValid = function(o) {
  return /abc/i.test(o.index);
};

var highest = _.maxBy(jsonDataWithoutValidValues , isValid);

if (isValid(highest)) {
  console.log('The max value is: ' + highest.index);
} else {
  console.log('No valid value found!');
}
<script src="https://cdn.jsdelivr.net/lodash/4/lodash.min.js"></script>

This is a probably a bit weird to use in production, but I thought it was interesting enough to share.

Upvotes: 2

nem035
nem035

Reputation: 35491

If you want to find the highest abc{SOME_NUMBER} value and filter at the same time, you can just use regular iteration instead of _.filter:

let jsonData = [{"index": "xyz"},{"index": "abc1234"}, {"index": "xyz"},{"index": "abc5678"}];

let max = Number.MIN_SAFE_INTEGER; // value for the max number at the end of "abc"
let item;                          // item containing the max abc${NUMBER} value
let filtered = [];                 // filtered array containing abc strings

jsonData.forEach((curr) => {

  // filter test
  if (/abc/i.test(curr.index)) {
    filtered.push(curr);

    // max value test
    const [digits] = curr.index.match(/\d+/);
    const test = parseInt(digits);
    if (test > max) {
      max = test;
      item = curr;
    }
  }
});

console.log('Item:\n', item, '\n\n----\nFiltered:\n', filtered);

Upvotes: 2

Ulysses
Ulysses

Reputation: 6015

Following is a crude and unsatisfactory implementation:

//filter out matching objects for possible future use
var latest = "";
var matches = _.filter(jsonData, function (o) { 
    var ret = /abc/i.test(o.index);
    if (ret) {            
        var digits = o.index.replace(/\D/g,'')            
        if (parseInt(digits) > latest) {
            latest = digits;
            latestIndex = o.index
            console.log(latest+">>>latestIndex")
        }
        return true;
    }
    return false;
    });
    console.log("latestIndex->"+latest);
}

Upvotes: 2

Related Questions