Valip
Valip

Reputation: 4620

Javascript filter values from array

I have an array of arrays and each of those arrays contain two elements. How can I return the second element from array by filtering the first element?

For example I have the following array and my function should return the second element from each sub-array where the first item is 8:

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];

Expected result: ['first', 'second', 'third']

This is what I've implemented so far, but it returns both elements instead of just the second one from each array with a match...

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = array.filter(function(item){
  if(item[0] == 8) {
    return item[1];
  }
});
console.log(result);

Upvotes: 2

Views: 24029

Answers (7)

Andrzej Smyk
Andrzej Smyk

Reputation: 1724

Array.prototype.filter returns element that passes provided predicate, so in you case that will be item for which item is true. To achieve what you want, you need to filter items first and the extract second element using map:

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = array.filter(function(item) {
  return item[0] == 8;
}).map(function (item) {
  return item[1];
});
console.log(result);

Or use reduce:

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = array.reduce(function(result, item) {
  return item[0] == 8 ? result.concat(item[1]) : result;
}, []);
console.log(result);

If you want to use features of ES6 it can get event shorter:

const array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
const result1 = array.filter(([item]) => (item == 8)).map(([,item]) => item);
const result2 = array.reduce((res, [n, str]) => n == 8 ? [...res, str] : res, []);

console.log(result1);
console.log(result2);

EDIT: To understand why your function is not working you need to understand how Array.prototype.filter() works. It creates a new array made of elements for which provided predicate function (i.e. checking some condition and returning Boolean) returns true. For all sub-arrays that have 8 as first item, second element will decide if that element will be used for creating new array.

So for each element of array (first [8, 'first'], next [8, 'second'] etc.) your function will be evaluated, and element for which true is returned, will be added to new array:

[[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']]
true,          true,          false,        true,         false

=> [[8, 'first'], [1, 'empty'], [8, 'third']]

Upvotes: 2

Lokesh Boran
Lokesh Boran

Reputation: 99

You can utilize Array.reduce to achieve your desired output -

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];


array.reduce(function(i, n){
    if(n[0]==8){
        i.push(n[1]);
    }
    return i;
},[]);
//output : ['first', 'second', 'third']

Upvotes: -2

Nina Scholz
Nina Scholz

Reputation: 386604

You could filter with the condition and then you need to map only the part, you want.

In a single loop it is only possible with Array#reduce, because here you can manipulate the value for the result set.

Array#filter returns the original item of the array, without changing something.

The filter() method creates a new array with all elements that pass the test implemented by the provided function.

For filtering, the callback needs to return a value which is interpreted as boolean, that means, every truthy value (like an object, array, or any not zero number, etc) takes the item for the result set.

If you like to use only a part of the inner array and the part is a truty value, then filtering works, but it does not take the return value, because it is not made for that.

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']],
    result = array.filter(function(item){
        return item[0] === 8;
    }).
    map(function (a) {
        return a[1];
    });

console.log(result);

Upvotes: 4

Zhendong
Zhendong

Reputation: 99

You could use function reduce

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];

var result = array.reduce(function(acc, item){
  if(item[0] === 8){
    acc.push(item[1])
   }
   return acc;
}, []);

console.log(result);

Array#reduce It will finish in a single loop.

Upvotes: 1

T.J. Crowder
T.J. Crowder

Reputation: 1074335

You need to both filter out the entries you don't want and map the entries to just the second value in the entry.

In general, the best way is to filter then map:

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = array.filter(function(item){ return item[0] == 8; })
                  .map(function(item) { return item[1]; });
console.log(result);

...but for a really large data set, if you don't want to make multiple passes, you can give yourself a filterMap function:

function filterMap(arr, predicate) {
    var result = [];
    arr.forEach(function(entry) {
      var newEntry = predicate(entry);
      if (newEntry !== undefined) {
          result.push(newEntry);
      }
    });
    return result;
}
var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = filterMap(array, function(item){
    return item[0] == 8 ? item[1] : undefined;
});
console.log(result);

That version assumes you never want an undefined entry in the result, but you can tweak as desired. For instance, if you want it to be really general purpose, you'd put a unique value on filterMap which indicated "leave this out":

function filterMap(arr, predicate) {
    var result = [];
    arr.forEach(function(entry) {
      var newEntry = predicate(entry);
      if (newEntry !== filterMap.OMIT) {
          result.push(newEntry);
      }
    });
    return result;
}
filterMap.OMIT = {};
var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = filterMap(array, function(item){
    return item[0] == 8 ? item[1] : filterMap.OMIT;
});
console.log(result);

While you can shoehorn reduce to do this, reduce isn't really great for situations where the "accumulator" doesn't change. So if you do this frequently, give yourself a utility to do it. Or just filter and map.

Upvotes: 0

sjahan
sjahan

Reputation: 5950

First map, then filter to remove undefined values.

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 'empty']];
var result = array.map(function(item){
  if(item[0] == 8) {
    return item[1];
  }
}).filter(function(item) {
    return item;
});
console.log(result);

Upvotes: 0

SsNewbie
SsNewbie

Reputation: 265

Array's filter method creates a new array by passing the item that pass certain condition in each iteration.

Try out this:

var array = [[8, 'first'], [8, 'second'], [1, 'empty'], [8, 'third'], [9, 
'empty']],resArray = [];
array.forEach(function(item){
  if(parseInt(item[0]) === 8){
    resArray.push(item[1]);
  }
});
console.log(resArray);

Upvotes: 0

Related Questions