Query array of objects in JavaScript - javascript

I have an array of coordinates like this:
coordinates = [
{x: 1, y: 2},
{x: 3, y: 4},
{x: 5, y: 6},
{x: 7, y: 8},
{x: 9, y: 0}
];
I want to query this array for an object like this.
var searchFor = {x: 1, y: 2}
I tried this:
if ($.inArray(searchFor, coordinates) !== -1) {
...
}
But this always return -1. All I need is true/false info about whether the object is in this array. How can I achieve this?

This is because objects are not equal to each other - even if they have the same properties/values - unless they are the exact same instance.
What you would have to do is manually iterate through the array:
for( var i=0, l=coordinates.length, found = false; i<l; i++) {
if( coordinates[i].x == searchFor.x && coordinates[i].y == searchFor.y) {
found = true;
break;
}
}
if( found) {
// ...
}

If you want a convenient one-liner solution, you could work with Lo-Dash.
_(coordinates).findIndex({x: 3, y: 4})
// 1

Here's a more generic approach for searching for an object within the array of objects:
Array.prototype.indexOfObj = function(o,exact){
// make sure incoming parameter is infact an object
if (typeof o === 'object'){
// iterate over the elements of the origin array
for (var i = 0; i < this.length; i++){
var match = true,
to = this[i],
matchedKeys = [];
// search through o's keys and make sure they exist and
// match the keys in the origin array
for (var k in o){
match &= o.hasOwnProperty(k) && to.hasOwnProperty(k);
if (match){
matchedKeys.push(k);
match &= (k in to && to[k] == o[k]);
}
}
// if we need an exact match, map it backwards as well
// (all of o's keys == all of to's keys)
if (match && exact){
for (var k in to){
match &= to.hasOwnProperty(k);
// additional unmatched keys
if (match && matchedKeys.indexOf(k) == -1){
match = false;
break;
}
}
}
// if it was a match, return the current key
if (match){
return i;
}
}
}
// default to to match found result
return -1;
}
Then, using your example:
{x:98,y:99} non-exact = -1
{x:98,y:99} exact = -1
{x:1} non-exact = 0
{x:1} exact = -1
{x:5,y:6} non-exact = 2
{x:5,y:6} exact = 2

use taffy DB, Taffy DB
var coordinates = [ {x: 1, y: 2}, {x: 3, y: 4}, {x: 5, y: 6}, {x: 7, y: 8}, {x: 9, y: 0}];
var coordinatesDB = TAFFY(coordinates);
res = coordinatesDB({x: 1, y: 2});

You could use $.grep - http://api.jquery.com/jQuery.grep/
coordinates = [{x: 1, y: 2}, {x: 3, y: 4}, {x: 5, y: 6}, {x: 7, y: 8}, {x: 9, y: 0}];
var query = $.grep(coordinates, function(co){ return co.x == 1 && co.y == 2; });
var hasResult = (query.length !== 0)
// query = {x: 1, y:2} - hasResult = true

As mentioned by others, you can not compare two unique objects contents by comparing the objects themselves, so you have to compare their properties. You could do something like this with Array.prototype.some which is ECMA5 but can easily be shimmed.
Javascript
function indexOfCoordinates(array, object) {
var index = -1;
array.some(function (coordinate, arrayIndex) {
if (coordinate.x === object.x && coordinate.y === object.y) {
index = arrayIndex;
return true;
}
return false;
});
return index;
}
var coordinates = [
{x: 1, y: 2},
{x: 3, y: 4},
{x: 5, y: 6},
{x: 7, y: 8},
{x: 9, y: 0}
];
if (indexOfCoordinates(coordinates, {x: 5, y: 6}) !== -1) {
console.log("found");
}
if (indexOfCoordinates(coordinates, {x: 9, y: 1}) === -1) {
console.log("not found");
}
On jsfiddle
Or as you suggested, you only want true or false then you can further simplify.
Javascript
function hasCoordinate(array, object) {
return array.some(function (coordinate) {
return coordinate.x === object.x && coordinate.y === object.y;
});
}
var coordinates = [
{x: 1, y: 2},
{x: 3, y: 4},
{x: 5, y: 6},
{x: 7, y: 8},
{x: 9, y: 0}
];
if (hasCoordinate(coordinates, {x: 1, y: 2})) {
console.log("found");
}
if (!hasCoordinate(coordinates, {x: 9, y: 1})) {
console.log("not found");
}
On jsfiddle
This could be further generalised using ECMA5 methods Object.keys and Array.prototype.map, should you for example, change the references x and y to a and b, or extend your coordinates to include z. Now your function would still work without need of alteration.
Javascript
function hasCoordinate(array, object) {
var objectKeys = Object.keys(object).sort(),
objectValues = objectKeys.map(function (value) {
return object[value];
});
return array.some(function (coordinate) {
var coordinateKeys = Object.keys(coordinate).sort(),
coordinateValues = coordinateKeys.map(function (value) {
return coordinate[value];
});
return coordinateKeys.toString() === objectKeys.toString() && coordinateValues.toString() === objectValues.toString();
});
}
var coordinates = [
{x: 1, y: 2},
{x: 3, y: 4},
{x: 5, y: 6},
{x: 7, y: 8},
{x: 9, y: 0}
];
if (hasCoordinate(coordinates, {x: 1, y: 2})) {
console.log("found");
}
if (!hasCoordinate(coordinates, {x: 9, y: 1})) {
console.log("not found");
}
On jsfiddle
Of course you could continue further along the generic route, and even introduce recursion.

Related

Search element in array of objects

Could someone guide me how to find an element in an array?
I always get false for elements that exist.
Thank you.
arr = new Array;
arr.push({x: "a", y: 1});
arr.push({x: "b", y: 2});
arr.push({x: "c", y: 3});
​
elem = {x: "c", y: 3};
​
if (arr.includes(elem)){
console.log('true');
} else {
console.log('false');
}
You are looking for {x: "c", y: 3} object in arr and It will never be equal to any other object because {} !== {}.
Remember: Object are compared with reference but the primitives are checked by their value.
In your code you are checking an object reference with another reference, which is not equal so that's why it logs false.
If there are array of numbers then you should use includes as:
const arr = [10, 20, 30];
if(arr.includes(20)) console.log("true");
else console.log("false");
You can use find to find the element in the array
const arr = new Array();
arr.push({ x: "a", y: 1 });
arr.push({ x: "b", y: 2 });
arr.push({ x: "c", y: 3 });
const elem = { x: "c", y: 3 };
if (arr.find((o) => o.x === elem.x && o.y === elem.y)) {
console.log("true");
} else {
console.log("false");
}
since you are working with a 2d array i believe your object will be allways same for X and Y , so i suggest you to make a function like that for further use in your code
arr = new Array;
arr.push({x: "a", y: 1});
arr.push({x: "b", y: 2});
arr.push({x: "c", y: 3});
elem = {x: "c", y: 3};
console.log(findXY(arr,elem))
function findXY(arr,val){
return arr.find(each=>{
return each.x == val.x && each.y == val.y
})
}

Find object within array of arrays and return the array it belongs to

I have the following array structure:
const array = [array1, array2, array3];
Each one of the three arrays consists of objects of form:
array1 = [{x: 0, y: 1}, {x: 5, y: 9}, {x: 1, y: 8}, {x: 3, y: 2}, etc]
I am trying to find the most efficient way to go through arrays of array and return the array to which a particular (unique) object belongs. For example I have object
{x:9, y:5}
which can be uniquely found in array2, so I want to return array2.
here's what I've tried:
const array = [array1, array2, array3];
for (let x = 0; x < array.length; x++) {
for (let y = 0; y < array[x].length; y++) {
array[x].find(e => e === array[x][y])
return array[x];
}
}
You'll need two loops, but you can use methods that do the iteration for you:
let array1 = [{x: 0, y: 1}, {x: 5, y: 9}, {x: 1, y: 8}, {x: 3, y: 2}];
let array2 = [{x: 5, y: 4}, {x: 4, y: 5}, {x: 8, y: 8}, {x: 3, y: 2}];
let array3 = [{x: 4, y: 3}, {x: 0, y: 6}, {x: 7, y: 8}, {x: 5, y: 2}];
const array = [array1, array2, array3];
let obj = array2[2]; // let's find this one...
let result = array.find(arr => arr.includes(obj));
console.log(result);
Here use find
data = [
[{x:1, y:2}, {x:2, y:3}],
[{x:3, y:2}, {x:4, y:3}],
[{x:5, y:2}, {x:6, y:3}],
[{x:7, y:2}, {x:8, y:3}]
];
const getArray = ({x, y}) => data.find(a => a.some(o => o.x === x && o.y === y));
console.log(getArray({x:3, y:2}));
TLDR; There is a working example in this fiddle
This can be accomplished using the following 3 things:
a library such as lodash to check for object equality (https://lodash.com/docs/4.17.15#isEqual)
The reason for this is that the behaviour of directly comparing two objects is different than you might think more info here
array.findIndex to find the index of the outer array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex)
array.find to find the element in an inner array (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find)
The following method findObjectInNestedArray will do what you'd like.
const findObjectArray = (obj, arr) => {
index = arr.findIndex(a => a.find(e => _.isEqual(e, obj)))
return arr[index] // will return `undefined` if not found
}
// Example code below
const array1 = [{x: 0, y: 1}, {x: 5, y: 9}, {x: 1, y: 8}, {x: 3, y: 2}];
const array2 = [{x: 1, y: 1}, {x: 2, y: 2}, {x: 3, y: 3}, {x: 4, y: 4}, {x:9, y:5}];
const array3 = [{x: 5, y: 5}];
const arrays = [array1, array2, array3];
const inArray2 = {x:9, y:5};
const notInAnyArray = {x:0, y:0};
console.log('array2', findObjectArray(inArray2, arrays));
console.log('not in array', findObjectArray(notInAnyArray, arrays));
I know I said a single iteration was impossible before, but I devised a possible method that could work under specific circumstances. Essentially, you can sort the properties then stringify the objects for instant lookups. The sort is necessary to ensure you always get a consistent stringified output regardless of the object's properties preexisting order. There are three caveats to this method:
the objects CANNOT contain functions. Properties with functions are dropped in the stringification process.
NaN and infinity are converted to null, which can cause unexpected "matches" in the cache
If the depth of the object is not known (i.e. the target objects can contain references to arrays and other objects), then you'll need to deeply traverse through every level before stringifying.
It's a trade-off that's only improves performance when comparing deeply nested or extremely large objects. It's scalable, though, I guess.. Here's an example of how it could be done:
// sort's an array's values, handling subarrays and objects with recursion
const sortArr = arr => arr.sort().map(el => typeof el === 'object' ? (Array.isArray(el) ? sortArr(el) : sortObj(el)) : el)
// sorts a key's objects, then recreates the object in a consistent order
const sortObj = obj => Object.keys(obj).sort().reduce((final, prop) => {
final[prop] = (
// if it's an object, we'll need to sort that...
typeof obj[prop] === 'object'
? (
Array.isArray(obj[prop])
? sortArr(obj[prop])//<-- recursively sort subarray
: sortObj(obj[prop])//<-- recursively sort subobject
)
// otherwise, just retrun the value
: obj[prop]
)
return final
}, {})
// for every element in the given array, deeply sort then stringify it
const deepSortObjectArray = (arr) => arr.map(el => JSON.stringify(sortObj(el)))
// from those strings, create an object with the strings as values and an associated 'true' boolean
const obejctCache = (obj) => deepSortObjectArray(obj).reduce((acc, el) => ({[el]: true, ...acc}), {})
// create an object string cache for every object in the array:
const cacheObjectArrays = arr => arr.map(obj => obejctCache(obj))
// perform an O(1) lookup in each of the caches for a matching value:
const findArrayContainer = (obj, caches) => {
const stringLookupObj = JSON.stringify(sortObj(obj))
return caches.findIndex(cache => cache[stringLookupObj])
}
const array = [
{y: 1, x: 0},
{x: 5, y: 9},
{x: 1, y: 8},
{x: 3, y: {z: 3, x: 1, y: 2}}
]
const arrayArray = [[], [], array]
const cachesArrays = cacheObjectArrays(arrayArray)
console.log(cachesArrays)
/* output: [
{},
{},
{ '{"x":3,"y":{"x":1,"y":2,"z":3}}': true,'{"x":1, "y":8}': true, '{"x":5,"y":9}': true,'{"x":0,"y":1}': true }
]
*/
console.log(findArrayContainer({y: 1, x: 0}, cachesArrays))
// output: 2; working normally!
console.log(findArrayContainer({x: 0, y: 1}, cachesArrays))
// output: 2; working regardless of order!
console.log(findArrayContainer({y: 1, x: 0, q: 0}, cachesArrays))
// output: -1; working as expected with non-found objects!
As you can see, it's pretty complicated. Unless you're 100% this is actually the performance bottleneck, these performance gains may not translate to making interaction smoother.
Let me know if you have any questions about it!

Is it possible to search a object in an array with both of its properties

I have the following code
const xPosition = coordinates.find(position => position.x === avoidObstacleX);
This returns me the coordinates {x: 26, y: 10} this is not wrong, but I have another coordinate that is the one I will like to output which is {x: 26, y: 11} Is there a way I can pass two parameters to the find method?
You could use two variables (not parameters to the find method itself), like you already use one:
function findObstacle(coordinates, avoidObstacleX, avoidObstacleY) {
return coordinates.find(position => position.x === avoidObstacleX
&& position.y === avoidObstacleY);
}
const xyPosition = findObstacle(coordinates, avoidObstacleX, avoidObstacleY);
But from the other answer I now learn that there are two interpretations of your question...
find only retrieves a single element, you need to use the filter method:
const coordinates = [ {x: 26, y: 10}, {x: 26, y: 11}, {x: 12, y: 34} ]
const avoidObstacleX = 26;
// returns [ {x: 26, y: 10}, {x: 26, y: 11} ]
const xPosition = coordinates.filter(position => position.x === avoidObstacleX);
To pass one value:
const coordinates= [
{x: 26, y: 10},
{x: 26, y: 11},
{x: 36, y: 6},
{x: 7,y: 8}
]
const avoidObstacleX=26;
let result = coordinates.filter(position=> {
return position.x === avoidObstacleX ;
})
console.log(result)
You can pass two values:
const coordinates= [
{x: 26, y: 11},
{x: 26, y: 11},
{x: 26, y: 11},
{x: 7,y: 8}
]
function find(avoidObstaclex,avoidObstacley){
let result= coordinates.filter(position=> {
return position.x === avoidObstaclex && position.y === avoidObstacley ;
})
return result;}
const avoidObstacleX=26;
const avoidObstacleY=11;
console.log(find(avoidObstacleX,avoidObstacleY))

Compare array of objects and return index of object

I've got this array of objects:
[
{x: 615, y: 293, a: 1},
{x: 340, y: 439, a: 0},
{x: 292, y: 505, a: 0}
]
Basically im trying to write a collider. I'd love to return indexes of objects that values of x and y that are equal to each other, how do i approach this?
You can write a function that iterates, via map, through the array and returns the index or a null depending on this condition (x === y) and than filter it, returning only those that are different from null
const collider = array =>
array.map( (item, index) => item.x === item.y ? index : null )
.filter( item => item !== null)
Working fiddle:
https://jsfiddle.net/c3qqmh3b/
var indexes = myArray.reduce((idxs, el, i) => {
if (el.x === el.y) {
return idxs.concat(i);
} else {
return idxs;
}
}, []);
If your myArray would be, for example:
myArray = [
{x: 615, y: 293, a: 1},
{x: 340, y: 340, a: 0},
{x: 292, y: 505, a: 0}
]
then you'll get [1] as a result (because element of index 1 has x===y)
You could just use a hashtable to check for duplicates:
const hash = {};
for(const {x, y} of array) {
if(hash[x + "|" + y])
alert("collides");
hash[x + "|" + y] = true;
}

Convert array of objects into array of primitives (extracted from object properties)

Consider the following code:
var input = [{x: 1, y: 6}, {x: 4, y: 3}, {x: 9, y: 2}];
var output = convert(input);
console.log(output); // = [1, 6, 4, 3, 9, 2]
What is the shortest, most concise convert function I can write that will give me the output shown?
So far I've come up with the following:
function convert(input) {
var output = [];
input.forEach(function(obj) {
output.push(obj.x, obj.y);
});
return output;
}
But surely there's a nice one-liner way of doing this?
With Array.prototype.reduce method it will save you two lines of code:
function convert(arr) {
return arr.reduce(function(prev, curr) {
return prev.concat(curr.x, curr.y);
}, []);
}
var input = [{x: 1, y: 6}, {x: 4, y: 3}, {x: 9, y: 2}];
document.write(JSON.stringify( convert(input) ));

Categories

Resources