2

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);

2
  • filter doesn't map - I think you want map (and filter to filter out the unwanted results) Commented Oct 11, 2017 at 7:59
  • I agree with the comment above: i think you need to reread the documentation on filter. Based on the return of your filtering function, it will keep or not the current element. It won't modify it or use the object you return instead. Commented Oct 11, 2017 at 8:02

7 Answers 7

4

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);

Sign up to request clarification or add additional context in comments.

3 Comments

Can you explain why this is not working? var ans= array.filter((a)=> {if(a[0]==8) return a[1]})
Array#filter filters the array, removing elements which fail the "filter test", but does not change the array in other ways. Array#map changes the array, but does not remove elements
@Valip, you could later slice the array with result = result.slice(0, 2) for the first 2 elements.
2

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']]

2 Comments

Can you explain why this is not working? var ans= array.filter((a)=> {if(a[0]==8) return a[1]})
I have updated my answer. If have more questions let me know
1

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.

Comments

0

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);

Comments

0

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);

Comments

0

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.

Comments

-2

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']

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.