Adding object values based on matching keys - javascript

I have an array of objects and I'm trying to combine like keys and add the values. So X should be 0, Y should be 1, and B should be 3. Thanks for any help!!!!
const arr = [{X: -1}, {Y: 1}, {X: -4}, {B: 3}, {X: 5}];
let result = {};
for (let i = 0; i < arr.length; i++) {
var item = arr[i];
for (var key in item) {
if (!(key in result))
parseInt(item);
result[key] = [];
result[key] += item[key];
}
}
console.log(result);
I expected X to be 0 but instead it is returning 5.

You can reduce each item (object) by grabbing the key and assigning the added previous value with the current value.
const input = [ {X: -1}, {Y: 1}, {X: -4}, {B: 3}, {X: 5} ];
let response = input.reduce((obj, item) => {
return ((key) => Object.assign(obj, {
[key] : (obj[key] || 0) + item[key] // Add previous with current
}))(Object.keys(item)[0]);
});
console.log(response);
.as-console-wrapper { top: 0; max-height: 100% !important; }
Result
{
"X": 0,
"Y": 1,
"B": 3
}
Fun Code Golf Experiment
I changed Object.assign(o,{[k]:(o[k]||0)+e[k]}) to ({...o,[k]:(o[k]||0)+e[k]}) by utilizing the spread operator to save 10 bytes.
r=i=>i.reduce((o,e) =>(k=>({...o,[k]:(o[k]||0)+e[k]}))(Object.keys(e)[0])) // 74 bytes
console.log(r([{X:-1},{Y:1},{X:-4},{B:3},{X:5}]))
.as-console-wrapper { top: 0; max-height: 100% !important; }

You could use Array.prototype.reduce with Object.entries to group by key in order to summate the values.
Example below (check the comments for more details):
const arr = [{
X: -1
}, {
Y: 1
}, {
X: -4
}, {
B: 3
}, {
X: 5
}];
//Iterate the object els in the arr
const map = arr.reduce((accum, el) => {
//Destructure the object into some clearly defined variables
const [
[key, value]
] = Object.entries(el);
//Check the key against the map
if (accum[key] != null) {
//Add the value to the existing map value
accum[key] += value;
} else {
//Set the initial value in the map
accum[key] = value;
}
return accum;
}, {});
console.log(map);

Here is inner loop changed such that we access the key, if exists, it's used; otherwise it's initialized to zero. Then value is added.
const arr = [{X: -1}, {Y: 1}, {X: -4}, {B: 3}, {X: 5}];
let result = {};
for (let i = 0; i < arr.length; i++) {
var item = arr[i];
for (var key in item) {
result[key] = (result[key] || 0) + item[key] // changed here
}
}
console.log(result);
{X: 0, Y: 1, B: 3}

Simple solution:
const arr = [{X: -1}, {Y: 1}, {X: -4}, {B: 3}, {X: 5}];
let result = {};
for (let i = 0; i < arr.length; i++) {
var item = arr[i];
for (var key in item) {
if (result[key]) { // if key exists
result[key] += parseInt(item[key]);
} else { // if key doesn't exist
result[key] = parseInt(item[key]);
}
}
}
console.log(result);

a bit later but:
const arr = [{X: -1}, {Y: 1}, {X: -4}, {B: 3}, {X: 5}];
const result = arr.reduce((acc, item) =>{
let currentKey = Object.keys(item)[0]
return acc[currentKey] ? acc[currentKey] += item[currentKey] : acc[currentKey] = item[currentKey], acc
}, {})
console.log(result)

Related

Format data for chart

I'm having some trouble formatting/transforming some simple data into a format that I can use to graph, and I'm hoping someone might help me solve. Currently, I have something like this
somedata=
{test1: {good: 3, bad: 2, redo: 2}}
{test2: {good: 4, bad: 3}}
{test3: {good: 3, redo: 4}}
into something like
series:
[{name: "good", data: [3,4,3]},
{name: "bad", data: [2,3,0]},
{name: "redo", data: [2,0,4]}]
I can grab the categories by using Object.keys(somedata) easy enough i.e. ['test1', 'test2', 'test3'] but having problem formatting the rest of the data. I tried something like
let combine = {};
Object.values(somedata).map((row) => {
for (const [key, value] of Object.entries(row)) {
combine.hasOwnProperty(key)
? combine[key].push(value)
: (combine[key] = [value]);
}
console.log("combined", combine);
});
but quickly realized that it won't add 0 when key doesn't exist, which is required for the chart to compare between the different series, such as bar charts. So, any help is appreciated.
You can first collect all unique values and then using array#reduce and other array methods generate all the values corresponding to each key in an object accumaltor.
const somedata = [{test1: {good: 3, bad: 2, redo: 2}}, {test2: {good: 4, bad: 3}}, {test3: {good: 3, redo: 4}}],
uniqueValues = [...new Set(
somedata.reduce((r,o) => {
Object.values(o).forEach(ob => {
r.push(...Object.keys(ob));
});
return r;
}, [])
)];
result = Object.values(somedata.reduce((r, o) => {
Object.values(o).forEach(ob => {
uniqueValues.forEach(k => {
r[k] = r[k] || { name: k, data: []};
ob[k] ? r[k].data.push(ob[k]): r[k].data.push(0);
});
});
return r;
},{}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this: for each test, group categories (can optionally restrict to a subset) - assume Zero for missing category
const someData = {test1: {good: 3, bad: 2, redo: 2}, test2: {good: 4, bad: 3}, test3: {good: 3, redo: 4}};
function prepMyGraphData(data, fields) {
let out = {
}
for (const [k, el] of Object.entries(data)) {
const _fields = new Set((fields || Object.keys(el)).concat(Object.keys(out)));
for (const f of _fields) {
const v = el.hasOwnProperty(f) ? el[f] || 0 : 0 ; // own field or 0
if (out.hasOwnProperty(f)) {
out[f].data.push(v) // existing category
}else{
out[f] = {name: f, data: [v]} // new category entry
}
}
}
return Object.values(out)
}
let fields = ['good', 'bad', 'redo']; // OR, undefined, for ALL own properties
const data = prepMyGraphData(someData, fields);

How transpose collection in a lodash way?

I do have a collection like this:
{
a: {
x: 1,
y: 2,
},
b: {
x: 3,
y: 4,
}
}
And I want to transpose like this:
{
x: {
a: 1,
b: 2,
},
y: {
a: 3,
b: 4,
}
}
Today i'm using
var result = {};
for (var c in value)
for (var r in value[c])
result[r][c] = value[c][r];
return result;
But what is the "lodash way" to do it?
You can use _.forOwn if you really want to use Lodash:
const value = {a:{x:1,y:2},b:{x:3,y:4}};
let result = {};
_.forOwn(value, (v, c) => _.forOwn(v, (w, r) => (result[r] = result[r] || {})[c] = w));
console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js"></script>

Iterating over an array of objects, summing values with the same index, and returning a new array of objects

I have an array of objects, something like this:
const data = [ // array1
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
],[ // array2
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
]
What needs to be accomplished is summing x from the array1 with x from the array2 that have the same index. Same goes for y and z. The final result should be a new array of objects containing the summed values.
Something like this:
[
[{totalXOne: 2}, {totalYOne: 4}, {totalZOne: 6}],
[{totalXTwo: 2}, {totalYTwo: 4}, {totalZTwo: 6}],
[{totalXThree: 2}, {totalYthree: 4}, {totalZThree: 6}],
]
Note: All arrays are the same length, and if a value is missing it will be replaced with 0)
I found something nice on MDN, but it's summing all x, y, z values, and it's returning single summed values, like this:
let initialValue = 0;
let sum = [{x: 1}, {x:2}, {x:3}].reduce(function(accumulator,currentValue) {
return accumulator + currentValue.x;
}, initialValue)
Output:
[
[{totalX: 3}, {totalY: 6}, {totalZ: 9}], // this is not what I need
]
Is there any way I can achieve this?
UPDATE
I'm receiving JSON from another source. It contains a property called allEmpsData mapping over it I get the necessary salaryDataand mapping over it I'm getting the NET|GROSS|TAX data.
let allReports = [];
setTimeout(() => {
allEmpsData.map(x => {
let reports = {};
let years = [];
let months = [];
let netArr = [];
let grossArr = [];
let mealArr = [];
let taxArr = [];
let handSalaryArr = [];
x.salaryData.map(y => {
years.push(y.year);
months.push(y.month);
netArr.push(y.totalNetSalary);
grossArr.push(y.bankGrossSalary);
mealArr.push(y.bankHotMeal);
taxArr.push(y.bankContributes);
handSalaryArr.push(y.handSalary);
})
reports.year = years;
reports.month = months;
reports.net = netArr;
reports.gross = grossArr;
reports.meal = mealArr;
reports.taxesData = taxArr;
reports.handSalaryData = handSalaryArr;
allReports.push(Object.assign([], reports));
});
}, 1000);
As I can tell, everything is working as it should, but the truth is,. I don't know any better. Then here goes the magic:
setTimeout(() => {
result = allReports.reduce((r, a) =>
a.map((b, i) =>
b.map((o, j) =>
Object.assign(...Object
.entries(o)
.map(([k, v]) => ({ [k]: v + (getV(r, [i, j, k]) || 0) }))
)
)
),
undefined
);
console.log(result);
}, 1500);
... and it returns an empty array in the node console, but if I console.log any other property from the updated code above, it's there. Any suggestions?
Here is a functional programming way to do it, using an intermediate ES6 Map:
const data = [[[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]], [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}],[{x: 1}, {y:2}, {z:3}]]];
const result = data[0].map( (arr, i) => Array.from(data.reduce( (acc, grp) => (
grp[i].forEach( o =>
Object.entries(o).forEach( ([k, v]) => acc.set(k, (acc.get(k) || 0) + v))
), acc
), new Map), ([k, v]) => ({ [k]: v })) );
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Explanation
To facilitate the explanation let's agree on some terms:
We have the input (an array), consisting of groups. Each group is an array consisting of rows. Each row consists of objects, each having one property/value pair.
The output does not have the group level, but it has the rows, again consisting of objects, each having one property/value pair.
So using these terms let's go through the code:
As the number of rows in the output array is equal to the number of rows in any of the groups, it seems a good start to map the rows of the first group, i.e. like data[0].map.
For each row in the output, we need to make sums, and reduce is a good candidate function for that job, so we call data.reduce. For the initial value of that reduce call I have passed an empty Map. The purpose is to fill that Map with key-sum pairs. Later we can then decompose that Map into separate objects, each having one of those key/sum pairs only (but that is for later).
So the reduce starts with a Map and iterates over the groups. We need to take the ith row from each group to find the objects that must be "added". So we take the row grp[i].
Of each object in that row we get both the property name and value with Object.entries(o). In fact that function returns an array, so we iterate over it with forEach knowing that we will actually only iterate once, as there is only one property there in practice. Now we have the key (k) and value v. We're at the deepest level in the input structure. Here we adjust the accumulator.
With acc.get(k) we can know what we already accumulated for a particular key (e.g. for "x"). If we had nothing there yet, it gets initialised with 0 by doing || 0. Then we add the current value v to it and store that sum back into the Map with acc.set(k, ....). Using the comma operator we return that acc back to the reduce implementation (we could have used return here, but comma operator is more concise).
And so the Map gets all the sums per key. With Array.from we can iterate each of those key/sum pairs and, using the callback argument, turn that pair into a proper little object (with { [k]: v }). The [k] notation is also a novelty in ES6 -- it allows for dynamic key names in object literals.
So... Array.from returns an array of little objects, each having a sum. That array represents one row to be output. The map method creates all of the rows needed in the output.
You could use a helper function for getting a value of a nested object and map the values at the same index.
const getV = (o, p) => p.reduce((t, k) => (t || {})[k], o);
var data = [[[{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }]], [[{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }], [{ x: 1 }, { y: 2 }, { z: 3 }]]],
result = data.reduce((r, a) =>
a.map((b, i) =>
b.map((o, j) =>
Object.assign(...Object
.entries(o)
.map(([k, v]) => ({ [k]: v + (getV(r, [i, j, k]) || 0) }))
)
)
),
undefined
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try the following:
var arr1 = [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]];
var arr2 = [[{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}]];
var map = {
0 : 'x',
1 : 'y',
2 : 'z'
};
var map2 = {
0 :"One",
1 :"Two",
2 : "Three"
};
var result = [];
var obj= {};
for(var i = 0; i < arr1.length; i++){
total = 0;
var arr =[];
for(var j =0; j < arr1[i].length; j++){
obj["total"+ map[j] + map2[i]] = arr1[i][j][map[j]] + arr2[i][j][map[j]];
arr.push(obj);
obj = {};
}
result.push(arr);
}
console.log(result);
It's a good idea to try and break this sort of problems down into smaller problems, and build up gradually. This means we don't have to look at the whole thing in one go.
Let's write a function that adds together individual elements from an array:
function addElements(element1, element2, key, rowIndex) {
//for now we keep the keys the same, otherwise multiple additions
//won't work
return {
[key]: element1[key] + element2[key]
};
}
Now let's add two rows together, using our addElements():
function addRows(row1, row2, rowIndex) {
return ['x', 'y', 'z'].map((key, index) => {
// "key" will go through "x", "y", and "z" as
// "index" goes 0, 1, 2
const element1 = row1[index];
const element2 = row2[index];
return addElements(element1, element2, key, rowIndex);
});
}
Now we can iterate through all the rows in our first matrix, and add the equivalent from the second matrix using addRows():
function addMatrices(matrix1, matrix2) {
return matrix1.map((row1, index) => {
const row2 = matrix2[index];
return addRows(row1, row2, index);
});
}
Now we can turn this into a reducer:
const EMPTY_MATRIX = { ... }; //define a matrix of all zeroes here
matrices.reduce(addMatrices, EMPTY_MATRIX);
Hope this helps!
Try this simple and small code snipet:
const data = [ // array1
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
],[ // array2
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]
]
var array1 = data[0];
var array2 = data[1];
var returnArray = [];
array1.forEach(function (subArray1, index){
var subArray2 = array2[index];
var subReturn = [];
subArray1.forEach(function (obj, i) {
var variableVal;
if (i == 0){variableVal = "x";} else if (i == 1) {variableVal = "y";}
else if (i == 2) {variableVal = "z"}
var newObj = {};
newObj[variableVal] = obj[variableVal] + subArray2[i][variableVal];
subReturn[i] = newObj;
});
returnArray[index] = subReturn;
});
console.log(returnArray);
What you ask is basically known as zipWith function. So a generic solution could be laid as;
var data = [[[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]],
[[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}],
[{x: 1}, {y:2}, {z:3}]]],
zipWith = (a,b,f) => a.map((e,i) => f(e,b[i])),
zipper = (sa,sb) => sa.map((o,i) => Object.keys(o)
.reduce((r,k) => (r[k] = o[k] + sb[i][k], r), {})),
result = data.reduce((p,c) => zipWith(p,c,zipper));
console.log(result);
A bit shorter alternative:
var data = [ [ [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}] ],
[ [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}], [{x: 1}, {y:2}, {z:3}] ] ];
var result = data.reduce( (a, b) => a.map((_, i) =>
Array.from('xyz', (k, j) => [ { [k]: a[i][j][k] + b[i][j][k] } ] ) ) );
console.log( JSON.stringify( result ).replace(/]],/g, ']],\n ') );
This solution returns a single object with each key's value being added up.
const arr1 = [
[{x: 1}, {y: 2}, {z: 3}],
[{x: 4}, {y: 6}, {z: null}],
[{x: 5}, {y: 7}, {z: 9}]
]
const arr2 = [
[{x: 12}, {y: 20}, {z: 4}],
[{x: 13}, {y: 21}, {z: 3}],
[{x: 14}, {y: 22}, {z: 5}]
]
const arr3 = [
[{x: 2}, {y: 10}, {z: 67}],
[{x: 3}, {y: 31}, {z: 23}],
[{x: null}, {y: 25}, {z: null}]
]
function get_keys (arr) {
let keys = []
for (let i = 0; i < arr[0].length; i++) {
let key = Object.keys(arr[0][i])[0]
keys.push(key)
}
return keys
}
function sum_by_key (arrays) {
let res = {}
let keys = get_keys(arrays)
let all_obj = []
for (let i = 0; i < arrays.length; i++) {
for (let d = 0; d < arrays[i].length; d++) {
all_obj.push(arrays[i][d])
}
}
for (let i = 0; i < keys.length; i++) {
let k = keys[i]
res[k] = 0
for (let d = 0; d < all_obj.length; d++) {
let __k = Object.keys(all_obj[d])[0]
if (k === __k) {
res[k] += all_obj[d][__k]
}
}
}
return res
}
let arrays = [...arr1, ...arr2, ...arr3]
console.log(sum_by_key(arrays)) //=> { x: 54, y: 144, z: 114 }

Combining arrays for use cases

Node.js app, writing validation tests. Given the following:
var obj = { foo: null, bar: null, baz: null},
values = [ 0, 1];
I need to create n number of objects to account for every property being assigned every combination of possible values, to represent every possible use case. So for this example, the output should be 2^3=8 objects, e.g.
[
{ foo: 0, bar: 0, baz: 0},
{ foo: 0, bar: 1, baz: 0},
{ foo: 0, bar: 1, baz: 1},
{ foo: 0, bar: 0, baz: 1},
{ foo: 1, bar: 0, baz: 0},
{ foo: 1, bar: 1, baz: 0},
{ foo: 1, bar: 1, baz: 1},
{ foo: 1, bar: 0, baz: 1},
]
Underscore or lodash or other libraries are acceptable solutions. Ideally, I would like something like so:
var mapUseCases = function(current, remaining) {
// using Underscore, for example, pull the current case out of the
// possible cases, perform logic, then continue iterating through
// remaining cases
var result = current.map(function(item) {
// perform some kind of logic, idk
return magic(item);
});
return mapUseCases(result, _.without(remaining, current));
}
var myValidationHeadache = mapUseCases(currentThing, somethingElse);
Pardon my pseudocode, I think I broke my brain. ¯\_(ツ)_/¯
Solution for any object length and any values.
Please note, undefined values do not show up.
function buildObjects(o) {
var keys = Object.keys(o),
result = [];
function x(p, tupel) {
o[keys[p]].forEach(function (a) {
if (p + 1 < keys.length) {
x(p + 1, tupel.concat(a));
} else {
result.push(tupel.concat(a).reduce(function (r, b, i) {
r[keys[i]] = b;
return r;
}, {}));
}
});
}
x(0, []);
return result;
}
document.write('<pre>' + JSON.stringify(buildObjects({
foo: [0, 1, 2],
bar: [true, false],
baz: [true, false, 0, 1, 42]
}), 0, 4) + '</pre>');
One way is to count from "000" to "999" in a values.length-based system:
keys = ['foo','bar','baz']
values = ['A', 'B']
width = keys.length
base = values.length
out = []
for(var i = 0; i < Math.pow(base, width); i++) {
var d = [], j = i;
while(d.length < width) {
d.unshift(j % base)
j = Math.floor(j / base)
}
var p = {};
for(var k = 0; k < width; k++)
p[keys[k]] = values[d[k]]
out.push(p)
}
document.write('<pre>'+JSON.stringify(out,0,3))
Update for products:
'use strict';
let
keys = ['foo', 'bar', 'baz'],
values = [
['A', 'B'],
['a', 'b', 'c'],
[0, 1]
];
let zip = (h, t) =>
h.reduce((res, x) =>
res.concat(t.map(y => [x].concat(y)))
, []);
let product = arrays => arrays.length
? zip(arrays[0], product(arrays.slice(1)))
: [[]];
let combine = (keys, values) =>
keys.reduce((res, k, i) =>
(res[k] = values[i], res)
, {});
let z = product(values).map(v => combine(keys, v));
z.map(x => document.write('<pre>'+JSON.stringify(x)+'</pre>'))
This is a non-recursive version of what you want:
function createRange(keys, values) {
if (typeof values[0] !== typeof [])
values = keys.map(k => values);
var pointer = {};
var repeats = 1;
keys.forEach((k, i) => {
var vLen = values[i].length;
repeats *= vLen;
pointer[k] = {
get value() {
return values[i][pointer[k].current]
},
current: 0,
period: Math.pow(vLen, i),
inc: function() {
var ptr = pointer[k];
ptr.current++;
if (ptr.current < vLen) return;
ptr.current = 0;
if (i + 1 === keys.length) return;
var nk = keys[i + 1];
pointer[nk].inc()
}
};
});
var result = [];
for (var i = 0; i < repeats; i++) {
var o = {};
result.push(o);
keys.forEach(k => o[k] = pointer[k].value)
pointer[keys[0]].inc();
}
return result;
}
var objKeys = ['u', 'v', 'w', 'x', 'y', 'z'];
var objValues = [
['1', '2', '3'],
['a', 'b', 'c'],
['foo', 'bar', 'baz'],
[1, 3, 2],
['test', 'try', 'catch'],
['Hello', 'World'],
];
var range = createRange(objKeys, objValues);
range.map(v => document.write(JSON.stringify(v).big()))

Query array of objects in 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.

Categories

Resources