user4079725
user4079725

Reputation:

Return index value from filter method javascript

I have an array of objects in my angular controller.

I want to return the value of the index of the field within the array which has a matching ID to my parameter.

There will only be one object in the array with a matching fieldId..

$scope.indexOfField = function(fieldId) {
  return $scope.model.fieldData.filter(function(x) {
    if (x.Id === fieldId) return // ???????
  });
}

Upvotes: 87

Views: 230672

Answers (15)

A.R Naseef
A.R Naseef

Reputation: 2600

Use the findIndex method - Array.prototype.findIndex().

When the condition is met first, the index is returned.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

Upvotes: 5

Brian Patterson
Brian Patterson

Reputation: 1625

You most definitely can (ahem, guy who got accepted answer)

const polyfillter = (arr,condition) => arr.map( (e,i) => condition ? i : -1).filter(e=>e>=0)    

Pass in an array, and your condition, and get back matching keys.

example

mynewstuff = polyfillter(myoldcrud, "e.length > 5")

Upvotes: 2

Krishna Murthy
Krishna Murthy

Reputation: 137

['ab', 'cd', 'ef', 'id', 'junk', 'dummy','name'].map((x, ndx)=>['id', 'name'].includes(x)?ndx:'').filter(e=>e)

result: (2) [3, 6]

Upvotes: 0

JonyD
JonyD

Reputation: 71

Try flatMap(), with or without i.

[5, 12, 8, 130, 44].flatMap((e, i) => (e > 13 ? e : [])); // returns [130, 44]

Upvotes: 2

Ruben Nagoga
Ruben Nagoga

Reputation: 2218

You can't return index from filter method.

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

You can use forEach

$scope.indexOfField = function(fieldId) {
    var i;
    return $scope.model.fieldData.forEach(function(x, index) {
        if (x.Id === fieldId) {
            i = index;
        }
    });
    // use i
}

or even better to use for as you can't stop forEach when you have found your id.

$scope.indexOfField = function(fieldId) {
    var fieldData = $scope.model.fieldData, 
        i = 0, ii = $scope.model.fieldData.length;
    for(i; i < ii; i++) if(fieldData[i].Id === fieldId) break;
    // use i
}

Upvotes: 41

Jakub Keller
Jakub Keller

Reputation: 497

$scope.indexOfField = function(fieldId) {
  let index;
  $scope.model.fieldData.filter((x, i) => {
    if (x.Id === fieldId) index = i;
    return x.Id === fieldId;
  });
  return index;
}

Upvotes: -1

quime parle
quime parle

Reputation: 1046

The .findIndex() method returns the index of the first element of the array that satisfies a condition given by a function. If the function returns false for all elements of the array, the result is -1.

See the documentation here.

In my example, x is an item for each iteration and I use cross function for my condition.

const datas = [];
const fieldId = 5;
let index = datas.findIndex( x => x.Id === fieldId );

Upvotes: 103

Gabriel G
Gabriel G

Reputation: 121

If there is only one object returned you can simply use the original array to refer to the returned object.

Since filter returns an array, you can do as follows

$scope.indexOfField = function(fieldId) {
    var filteredArray = $scope.model.fieldData.filter(function(x) {
        return x.Id === fieldId
    });

    var desiredObject = filteredArray[0]
    return $scope.model.fieldData.indexOf(desiredObject);
}

Upvotes: 0

Foysal Ahmed Emon
Foysal Ahmed Emon

Reputation: 67

function modifyArray(nums) {
let newNums = nums.filter((num,index) => {
        return num = num % 2 ? num * 3 : num * 2; 
    })
  return newNums;
}

Here the index is the increment value you are looking for.

Upvotes: -3

JustAnotherDeveloper
JustAnotherDeveloper

Reputation: 593

You cannot return directly the index but you can set the 'thisArg' parameter and set data inside it. This is cleaner than a global variable.

var data = {
    indexes: []
};
var myArray = [1,2,3,4,5,6,7,8,9,10];

myArray.filter(function (element, index) {
    if (element%2 == 0) {
        this.indexes.push(index);
        return true;
    }
}, data);

console.log(data.indexes); // [1, 3, 5, 7, 9]
data.indexes.forEach(function(value) {
    console.log(myArray[value]);
}); // 2, 4, 6, 8, 10

Upvotes: 4

Dean Jones
Dean Jones

Reputation: 264

ARRAY (FIND MULTIPLE INDEXES) METHOD

[10, 7, 13, 15, 230].map((e,i) => e > 13 ? i : undefined).filter(x => x) 
      //returns [3, 4](*** RETURNS multiple indexes ***)
      //FILTER (is simply just REMOVING the UNDEFINED elements (which are FALSY and considered the same as FALSE)

otherwise you'll get...

[10, 7, 13, 15, 230].map((e,i) => e > 13 ? i : undefined)  //returns [undefined, undefined, undefined, 3, 4]

RETURN MULTIPLE INDEXES (replaces findIndex METHOD)

[1, 1, 2, 2, 2, 3, 4, 5].map((e,i) => e === 2 ? i : undefined).filter(x => x) //returns [2, 3, 4]

RETURN MULTIPLE VALUES (replaces find METHOD)

[5, 12, 8, 130, 44].map((e,i) => e > 13 ? e : undefined).filter(x => x) // returns [130, 44]

Upvotes: 22

Ferran Maylinch
Ferran Maylinch

Reputation: 11529

Some languages can map a collection into an indexed collection where each element is mapped to a pair of {element, index}. That way you can map/filter/etc using any of those two values.

For example, Kotlin has withIndex and Swift has enumerated.

Javascript doesn't have that method, AFAIK. But you can easily build yours or use a workaround.

Workaround (not recommended)

// Turn an array of elements into an array of {value, index}
const indexedArray = array.map((v,i) => ({value:v, index:i}));
// Now I can filter by the elem but keep the index in the result
const found = array.filter(x => x.value === someValue)[0];
if (found) {
    console.log(`${found.value} found at index ${found.index}`);
}

// One-liner for the question scenario, using some new js features.
// Note that this will fail at the last ".i" if the object is not found.
const index = fieldData.map((v,i) => ({v,i})).filter(x => x.v.id == fieldId)[0].i

Add a withIndex method to Array (recommended):

This is basically the same as the workaround, but creating a reusable function, which makes it much cleaner.

Array.prototype.withIndex = function() {
    return this.map((v,i) => ({value: v, index: i}))
};

// Now the one-liner would be:
const index = fieldData.withIndex().filter(x => x.value.id == fieldId)[0].index;

// Better, with null checking:
const found = fieldData.withIndex().filter(x => x.value.id == fieldId)[0];
if (found) {
    console.log(`${found.value} found at index ${found.index}`);
}

Upvotes: 1

Matt
Matt

Reputation: 35271

Filter will not return the index, but you can do something like this.

$scope.indexOfField = function(fieldId) {
    $scope.model.fieldData.filter(function(x, i) {
        if (x.Id === fieldId) {
            var indexOfField = i;
        }
    });
    return indexOfField;
};

Upvotes: -1

Jivings
Jivings

Reputation: 23260

From the Array.prototype.filter documentation:

callback is invoked with three arguments:

  • the value of the element
  • the index of the element
  • the Array object being traversed

However you should probably be using the some function if there is only one instance in your array (as it will stop as soon as it finds the first occurrence), and then find the index using indexOf:

var field = $scope.model.fieldData.filter(function(x) {
    return x.Id === fieldId;
})[0];
var index = $scope.model.fieldData.indexOf(field);

Or iterate the array until you find the correct element:

var index;
$scope.model.fieldData.some(function(x, i) {
    if (x.Id === fieldId) return (index = i);
});

Upvotes: 20

T.J. Crowder
T.J. Crowder

Reputation: 1075209

The second argument to your callback is the index. I can't quite make out what you want your function to do/return, but if you add , index after function(x, that will give you access to the index for that iteration.

Working from the name of your function, I don't think you want filter at all:

$scope.indexOfField = function(fieldId) {
    var result = -1;
    $scope.model.fieldData.some(function(x, index) {
        if (x.Id === fieldId) {
            result = index;
            return true;
        }
    });
    return result;
}

Array#some stops as of the first iteration that returns a truthy value, so we'll stop searching the first time we find a match.

Upvotes: 6

Related Questions