Javascript array.some: pass params to callback - javascript

I learned about array.some() and want to use it for a check if an object property is already set for any object in an array.
But I cannot get it working. I dont know how to pass the params to the callback function.
function hasPropertyValue(obj, property, value){
return (obj[property] === value);
}
let arr = [
{ id: 1, name: 'Name1'},
{ id: 2, name: 'Name2'}
];
console.log(arr.some(hasPropertyValue(element, 'id', 1))); //Uncaught ReferenceError: element is not defined

You could take a closure over the wanted key and value and return a function which gets the object from the calling method.
function hasPropertyValue(property, value) {
return function(object) {
return (object[property] === value);
};
}
let arr = [{ id: 1, name: 'Name1' }, { id: 2, name: 'Name2' }];
console.log(arr.some(hasPropertyValue('id', 1)));

The thing inside array.some needs to be a function, not the result of a function call. ie
arr.some(element => hasPropertyValue(element, 'id', 1));

By doing arr.some(hasPropertyValue(element, 'id', 1)) you pass the result of calling hasPropertyValue to .some, instead you want to pass the function itself to it. That could be done with arr.some(hasPropertyValue), but the arguments of .some do not match the parameters of hasPropertyValue. So you need to pass a function, that then calls your function:
function hasPropertyValue(obj, property, value){
return (obj[property] === value);
}
let arr = [
{ id: 1, name: 'Name1'},
{ id: 2, name: 'Name2'}
];
console.log(arr.some(element => hasPropertyValue(element, 'id', 1)));

array.some() method checks if at least one element in the array matches a condition defined in a 'validator function'.
It requires only the name of the validator function as the parameter.
Best practice is to use the validator function without arguments.
Here is a fix for the above mentioned problem:
// File name: array_some_demo.js
function hasPropertyValue(array_element) {
// Verify if the 'id' attribute of the element is 1
return (array_element.id === 1)
}
let arr = [
{ id: 100, name: 'Name100'},
{ id: 1, name: 'Name1'},
{ id: 2, name: 'Name2'}
]
// Call the .some method with 'hasPropertyValue' as the parameter.
// This will initiate a loop of hasPropertyValue on all elements.
//
// Execution breaks out of the loop
// when an element with 'id' value of 1 is found.
console.log(arr.some(hasPropertyValue))
Output:
$ node array_some_demo.js
true

Related

How do I get the indexOf an element using a criteria function

I'd like to get the indexOf an object in an array, by using a criteria function.
attempt1: works, but is inefficient, as I have to iterate the array twice.
attempt2: doesn't work (obviously), but indicates what I'd like to achieve.
const dataSet = [{ name: "obj1" }, { name: "obj2" }, { name: "obj3" }, { name: "obj4" }, { name: "obj5" }]
const attempt1 = dataSet.indexOf(dataSet.find(d => d.name === 'obj3'))
const attempt2 = dataSet.indexOf(d => d.name === 'obj3')
console.log(attempt1)
console.log(attempt2)
You're probably looking for findIndex
const dataSet = [{ name: "obj1" }, { name: "obj2" }, { name: "obj3" }, { name: "obj4" }, { name: "obj5" }]
const attempt2 = dataSet.findIndex(d => d.name === 'obj3')
console.log(attempt2)
Why second one is not working whereas first attempt is working ?
indexOf expects a searchElement value to be searched so in the first attempt you used find inside indexOf which returns a value whereas in second attempt you passed a function which not what indexOf expects

ES6 filter - how to return an object instead of an array?

I have bunch of array of object, I want to get particular object using filter, but I got array using below code.
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}]
const x = target.filter(o => o.id === 1)
console.log(x)
As said in the comments, filter won't allow you to get a particular object from an array - it just returns another array which elements satisfy the given predicate. What you actually need is Array.prototype.find(). Quoting the doc:
The find() method returns the value of the first element in the array
that satisfies the provided testing function. Otherwise undefined is
returned.
So your code looks like this:
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}];
const x = target.find(o => o.id === 1);
console.log(x); // {name: "abc", id: 1}
array.filter always return array. But you can try this-
const target = [{
name: 'abc',
id: 1
}, {
name: 'def',
id: 2
}]
let obj = {}
const x = target.filter( (o, index) => {
if(o.id === 1)
obj = target[index]
})
console.log(obj)
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
The find() method returns the value of the first element in the provided array that satisfies the provided testing function. If no values satisfy the testing function, undefined is returned.
Array.prototype.filter will return array containing elements from original array that passed test function.
If you are sure that id's are unique simply do x[0] to get result.
It's very easy just get first item in retrned as:
const target = [{name: 'abc', id: 1}, {name: 'def', id: 2}]
const x = target.filter(o => o.id === 1)
console.log(x[0])

For Loop in Recursive Function breaking without delivering result

Currently, I am using a recursive function within an existing angular controller to generate an array of indices based upon a tree structure, which is creating a "branch" to the specified target value. The intention of the function is to recursively check the values of a deeply nested object and loop through arrays as they may appear. The function works until it finds the target node and matches it. On the return call, which based upon what I know, should loop back up into the for loop. Instead, it exits the function and returns undefined. I did notice that the error thrown is generated by Angular, and is as follows.
angular.js:13236 ReferenceError: result is not defined
at questionsController.self.findIndex (questions-controller.js:724)
at questionsController.self.findIndex (questions-controller.js:734)
at questionsController.self.findIndex (questions-controller.js:724)
at questionsController.self.addQuestions (questions-controller.js:711)
at fn (eval at compile (angular.js:14086), <anonymous>:4:262)
at expensiveCheckFn (angular.js:15076)
at callback (angular.js:24546)
at Scope.$eval (angular.js:16820)
at Scope.$apply (angular.js:16920)
at HTMLButtonElement.<anonymous> (angular.js:24551)
The function is as follows:
self.findIndex = function (map, target, arr, index) {
if (map instanceof Array) {
for (var x = 0; x < map.length; x++) {
var newArr = arr.slice();
newArr.push(x);
result = self.findIndex(map[x], target, newArr, x);
if (result.length > 0) {
return result;
}
}
} else if (map instanceof Object) {
if (map.id == target.id) {
return arr;
}
else if (map.questions && map.questions.length > 0) {
return result = self.findIndex(map.questions, target, arr, index);
}
}
return [];
};
The data model is as follows:
[
{
id: 1,
name: 'foo',
questions: [
{
id: 2,
name: 'bar',
questions: []
}
]
},
{
id: 3,
name: 'foobar',
questions: [
{
id: 4,
name: 'barfoo',
questions: [
{
id: 5,
name: 'foobarfoo',
questions: []
},
{
id: 6,
name: 'barfoobar',
questions: []
}
]
}
]
}
]
Please let me know if any additional info regarding the question might help provide clarity.

Javascript: Filter array of objects

What am I doing wrong here?
var locations = [
{ id: 1, name: 'N'},
{ id: 2, name: 'P'}
]
var employee = { location_id: 1 }
locations.filter((location) => {
return location.id == employee.location_id
});
console.log(locations);
this returns undefined when I'm trying to make it return { id: 1, name: 'N'}.
filter() function is not mutable - which means it returns a new array with the filtered objects and do not 'mutate' the original array - you must assign it to another variable - see demo below:
locations = [
{ id: 1, name: 'N'},
{ id: 2, name: 'P'}
]
employee = { location_id: 1 }
var result = locations.filter((location) => {
return location.id == employee.location_id
})
console.log(result);
You need a variable for the result of filtering with Array#filter
The filter() method creates a new array with all elements that pass the test implemented by the provided function.
var locations = [
{ id: 1, name: 'N'},
{ id: 2, name: 'P'}
],
employee = { location_id: 1 },
result = locations.filter((location) => {
return location.id == employee.location_id
});
console.log(result);
You need to store the result of .filter(). It doesn't mutate the original array.
On a side note, you can shorten your callback function by removing the curly brackets and return statement.
locations = locations.filter(loc => loc.id == employee.location_id);

How can I get a unique array based on object property using underscore

I have an array of objects and I want to get a new array from it that is unique based only on a single property, is there a simple way to achieve this?
Eg.
[ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
Would result in 2 objects with name = bill removed once.
Use the uniq function
var destArray = _.uniq(sourceArray, function(x){
return x.name;
});
or single-line version
var destArray = _.uniq(sourceArray, x => x.name);
From the docs:
Produces a duplicate-free version of the array, using === to test object equality. If you know in advance that the array is sorted, passing true for isSorted will run a much faster algorithm. If you want to compute unique items based on a transformation, pass an iterator function.
In the above example, the function uses the objects name in order to determine uniqueness.
If you prefer to do things yourself without Lodash, and without getting verbose, try this uniq filter with optional uniq by property:
const uniqFilterAccordingToProp = function (prop) {
if (prop)
return (ele, i, arr) => arr.map(ele => ele[prop]).indexOf(ele[prop]) === i
else
return (ele, i, arr) => arr.indexOf(ele) === i
}
Then, use it like this:
const obj = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ]
obj.filter(uniqFilterAccordingToProp('abc'))
Or for plain arrays, just omit the parameter, while remembering to invoke:
[1,1,2].filter(uniqFilterAccordingToProp())
If you want to check all the properties then
lodash 4 comes with _.uniqWith(sourceArray, _.isEqual)
A better and quick approach
var table = [
{
a:1,
b:2
},
{
a:2,
b:3
},
{
a:1,
b:4
}
];
let result = [...new Set(table.map(item => item.a))];
document.write(JSON.stringify(result));
Found here
You can use the _.uniqBy function
var array = [ { id: 1, name: 'bob' }, { id: 2, name: 'bill' }, { id: 1, name: 'bill' },{ id: 2, name: 'bill' } ];
var filteredArray = _.uniqBy(array,function(x){ return x.id && x.name;});
console.log(filteredArray)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.5/lodash.js"></script>
In the above example, filtering is based on the uniqueness of combination of properties id & name.
if you have multiple properties for an object.
then to find unique array of objects based on specific properties, you could follow this method of combining properties inside _.uniqBy() method.
I was looking for a solution which didn't require a library, and put this together, so I thought I'd add it here. It may not be ideal, or working in all situations, but it's doing what I require, so could potentially help someone else:
const uniqueBy = (items, reducer, dupeCheck = [], currentResults = []) => {
if (!items || items.length === 0) return currentResults;
const thisValue = reducer(items[0]);
const resultsToPass = dupeCheck.indexOf(thisValue) === -1 ?
[...currentResults, items[0]] : currentResults;
return uniqueBy(
items.slice(1),
reducer,
[...dupeCheck, thisValue],
resultsToPass,
);
}
const testData = [
{text: 'hello', image: 'yes'},
{text: 'he'},
{text: 'hello'},
{text: 'hell'},
{text: 'hello'},
{text: 'hellop'},
];
const results = uniqueBy(
testData,
item => {
return item.text
},
)
console.dir(results)
In case you need pure JavaScript solution:
var uniqueProperties = {};
var notUniqueArray = [ { id: 1, name: 'bob' }, { id: 1, name: 'bill' }, { id: 1, name: 'bill' } ];
for(var object in notUniqueArray){
uniqueProperties[notUniqueArray[object]['name']] = notUniqueArray[object]['id'];
}
var uniqiueArray = [];
for(var uniqueName in uniqueProperties){
uniqiueArray.push(
{id:uniqueProperties[uniqueName],name:uniqueName});
}
//uniqiueArray
unique array by id property with ES6:
arr.filter((a, i) => arr.findIndex(b => b.id === a.id) === i); // unique by id
replace b.id === a.id with the relevant comparison for your case

Categories

Resources