6

What is JavaScript equivalent to Ruby's Array#compact?

Long version.... I followed the examples at blog.nemikor.com. His last example closes out old requests, but then pendings continues to be filled with obsolete requests. This looks like a memory leak to me.

My solution is to iterate over pendings with filter as below, but this seems like there may be a race condition between pendings.push and pendings = pendings.filter. Am I being paranoid? If a race condition exists, how should I fix it?

var pendings = [];

// there is a route
app.get('/some/path', function (request, response) {
  pendings.push({
    response: response,
    requestedAt: new Date().getTime()
  });
});

setInterval(function () {
  var expiration = new Date().getTime() - (1000 * 30);
  pendings = pendings.filter(function (pending, index) {
    if (pending.requestedAt > expiration) {
      return true;
    } else {
      pending.response.writeHead(408, { 'Content-Type': 'text/plain' });
      pending.response.end('');
    }
  });
}, 1000);
ravinggenius
  • 816
  • 1
  • 6
  • 14
  • 1
    [compact-like behavior](http://stackoverflow.com/questions/281264/remove-empty-elements-from-an-array-in-javascript) – Zabba May 30 '11 at 23:24
  • MDC doco on [filter](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/filter) if you need it. – RobG May 31 '11 at 00:36
  • Follow Zabba's link for an answer to the title question... – AShelly Nov 19 '12 at 16:16

4 Answers4

2

An old thread but it deserves an answer to the original question:

What is JavaScript equivalent to Ruby's Array#compact?

list1 = [null, 'no', 'nulls', null]

// With plain JS
jsFilter = list1.filter(function (obj) { return obj })

// With ES6
let filtered = list1.filter((obj) => obj)

//> (2) ["no", "nulls"]

console.log(es6Filter)

But there is a caveat, because:

filter() calls a provided callback function once for each element in an array and constructs a new array of all the values for which callback returns a value that coerces to true.

falsey = [false, 0, 0n, '', NaN, null, undefined]
truthy = [true, 1, ' ']

list2 = ['returns', 'only', 'truthy', 'values'].concat(truthy).concat(falsey)

let filtered = list2.filter((obj) => obj)

//> (7) ["returns", "only", "truthy", "values", true, 1, " "]

console.log(filtered)

In order to have it working like Ruby's compact, you can do:

falsey = [false, 0, 0n, '', NaN, null, undefined]
truthy = [true, 1, ' ']
list3 = ['works', 'like', "Ruby's", 'compact'].concat(truthy).concat(falsey)

let them_go = [null, undefined] // Just a fun with variable names
let filtered = list3.filter((obj) => { return !them_go.includes(obj) })

//> (12) ["works", "like", "Ruby's", "compact", true, 1, " ", false, 0, 0n, "", NaN]

console.log(filtered)

If you want to remove all the empty elements (empty Strings, Arrays, and Objects) from an array, you can check my answer here -> https://stackoverflow.com/a/59905255/3251051

References:

Zalom
  • 696
  • 9
  • 18
2

You have no threads in JavaScript, so there can be no race condition. All code is sequenced and will transfer control only after it's done running. So your interval function will run till completion before any other function is going to touch pendings.

This holds for things like setTimeout and setInterval.

As an experiment: If you made a timeout using setTimeout to fire after 1 second. And after that you write a while-loop that blocks for 2 seconds, your timeout will fire after that, so much longer than 1 second.

Something crude:

var timer = setTimeout(function () {
    alert("hi!");
}, 1000);
var now = new Date();
var till = new Date(now + 2);
while(new Date() < till) {}   // block for 2 seconds
Halcyon
  • 57,230
  • 10
  • 89
  • 128
2

You may want to take a look at the Underscore.js library
http://documentcloud.github.com/underscore/

This provides many useful low-level functions to deal with collections, arrays and objects. It includes both a compact function (though I think it serves a different purpose to what you're looking for) and a filter function.

Matty F
  • 3,763
  • 4
  • 30
  • 48
  • Thanks for suggesting underscore.js. I've looked at it, but didn't think about it as I haven't yet had a need for it. Node.js provides a filter method, and according to @frits-van-campen's answer, I don't need to worry about a race condition. :) – ravinggenius May 31 '11 at 02:01
1

As long as you're not doing I/O, i.e., you are only doing in-memory operations, you are guaranteed to not be interrupted (due to the nature of the event loop).

Being so, be careful, if your collection is too long (like on the thousands or more), because you can block the event loop for some time, not letting the other requests be serviced.

pgte
  • 51
  • 4