I have an array of objects having a boolean field X. [{..., x: true, y: 3, ...]
I need to aggregate this array, in order to obtain a value, true(or false), if all values of x are correspondingly true(or false), otherwise undefined... and a sum of y's...
is that possible to use the reduce Array function, groupby by underscorejs, or another one for this purpose?
ex:
[
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]
result
{x: undefined, y: 9}
this is pretty straight-forward with reduce:
.reduce((a, b) => ({
x: a.x == b.x ? a.x : undefined,
y: a.y + b.y
}))
Live example:
var input = [
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
];
console.log(input.reduce((a, b) => ({
x: a.x == b.x ? a.x : undefined,
y: a.y + b.y
})));
Although you can shoehorn this into a reduce call (because any array operation can be shoehorned into a reduce), there's no benefit to doing so. Just use a loop:
const result = {x: null, y: 0};
for (const entry of array) {
if (result.x === null) {
result.x = entry.x;
} else if (result.x !== entry.x) {
result.x = undefined;
}
result.y += entry.y;
}
Live Example:
function check(array) {
const result = {x: null, y: 0};
for (const entry of array) {
if (result.x === null) {
result.x = entry.x;
} else if (result.x !== entry.x) {
result.x = undefined;
}
result.y += entry.y;
}
console.log(result);
}
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: true, y: 6}
]);
console.log("---");
check([
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
But again, you can shoehorn that into a reduce if you want by always returning the same object:
const result = array.reduce((obj, entry) => {
if (obj.x === null) {
obj.x = entry.x;
} else if (obj.x !== entry.x) {
obj.x = undefined;
}
obj.y += entry.y;
return obj;
}, {x: null, y: 0});
Live Example:
function check(array) {
const result = array.reduce((obj, entry) => {
if (obj.x === null) {
obj.x = entry.x;
} else if (obj.x !== entry.x) {
obj.x = undefined;
}
obj.y += entry.y;
return obj;
}, {x: null, y: 0});
console.log(result);
}
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
check([
{a:'titi', x: true, y: 3},
{a:'toto', x: true, y: 6}
]);
console.log("---");
check([
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]);
console.log("---");
But, if you want a reduce solution and you don't mind creating a bunch of temporary throw-away objects, check out Adassko's answer. Simple and straight-forward, and 99.9% of the time, you don't care about the temporary object creation.
I came up with this solution using reduce. Seems kind of hacky, but it should do the job. While reducing the array it determines if every x-value is equal, afterwards it sets the x-value of the reduced object accordingly.
let reduced = arr.reduce((acc, curr) => {
acc.x &= acc.x_init === curr.x;
acc.y += curr.y;
}, {x_init: arr[0].x, x: true, y: 0});
reduced.x = reduced.x ? reduced.x_init : undefined;
delete reduced.x_init;
thanks #Adassko, my variant was a little bit longer:
[
{a:'titi', x: false, y: 3},
{a:'toto', x: false, y: 6}
]
.reduce((a, b, i) => ({
x : a.x === b.x || i == 0 ? b.x : undefined,
y : a.y + b.y
}))
Related
im trying to normalize some data sitting in an array of objects.
[
{id: 1, number: 10, x: 0.3, y: 0.4, …}
{id: 2, number: 5, x: 0.5, y: 0.2, …}
{...}
{...}
{...}
]
I want to map the x and y entry's on a new value between 0 - 1250. So I get the following Array of Objects
[
{id: 1, number: 10, x: 375, y: 500, …}
{id: 2, number: 5, x: 625, y: 250, …}
{...}
{...}
{...}
]
Whats the best Practice for that?
Best,
Chris
You can use Array.map
const arr = [
{id: 1, number: 10, x: 0.3, y: 0.4},
{id: 2, number: 5, x: 0.5, y: 0.2}
];
// Use Array.map to iterate
const arr1 = arr.map(ob => {
ob.x*=1250;
ob.y*=1250;
return ob;
});
console.log(arr1);
Some thing like this with map method.
const arr = [
{id: 1, number: 10, x: 0.3, y: 0.4},
{id: 2, number: 5, x: 0.5, y: 0.2},
];
const res = arr.map(({x, y, ...rest}) => ({...rest, x: x * 1250, y: y * 1250 }));
console.log(res)
Assuming arr is your array of object. You can use map which returns new modified array.
let arr = [
{
id: 1, number: 10, x: 0.3, y: 0.4,
},
{
id: 2, number: 5, x: 0.5, y: 0.2
}
];
const normalize = (obj) => {
x = obj.x * 1250;
y = obj.y * 1250;
return {...obj, x, y};
// If you're only using mutating then
// above lines can be
// obj.x *= 1250;
// obj.y *= 1250;
// return obj;
}
// Not mutating array, output new array
const nonMutating = (arr) => {
let newRes = [];
arr.forEach(a => {
newRes.push(normalize(a));
});
return newRes;
}
console.log(nonMutating(arr));
console.log("\n");
// Mutating input array
const mutating = (arr) => {
return arr.map(a => normalize(a));
}
console.log(mutating(arr));
Assume I have the following array of objects.
data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
]
what I need is to summarize the frequency of identical object in the array. The output will look like:
summary = [
{ x: 1, y: 1, f: 3 },
{ x: 1, y: 2, f: 1 },
{ x: 2, y: 2, f: 2 },
{ x: 3, y: 3, f: 1 }
]
For now I have this code
const summary = data.map((item, index, array) => {
return { x: item.x, y: item.y, f: array.filter(i => i === item).length };
});
But I suppose I can do better by using reduce or includes. Any ideas?
Reduce into an object whose keys uniquely represent an object, whose values are the object (with x, y, and f properties). On each iteration, increment the appropriate key's f property, or create the key on the accumulator if it doesn't exist yet:
const data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const countObj = data.reduce((a, obj) => {
const objString = obj.x + '_' + obj.y;
if (!a[objString]) {
a[objString] = { ...obj, f: 1 };
} else {
a[objString].f++;
}
return a;
}, {});
const output = Object.values(countObj);
console.log(output);
Don't use map - you're better off using reduce like so:
const summary = Object.values(data.reduce((a, { x, y }) => {
a[`${x}-${y}`] = a[`${x}-${y}`] || { x, y, f: 0 };
a[`${x}-${y}`].f++;
return a;
}, {}));
Object.values(data.reduce((sum, i) => {
i_str = JSON.stringify(i); // objects can't be keys
sum[i_str] = Object.assign({}, i, {f: sum[i_str] ? sum[i_str].f+1 : 1});
return sum;
}, {}));
Note:
This snippet will work on an array of any arbitrary objects, as long as they are stringifiable.
Results are not ordered, since object keys aren’t ordered. If this is an issue, sort at will.
What you’re doing, is counting the times an object exists in an array. You probably want results external to the objects, as opposed to embedded in them. Something along these lines might be more manageable, returning a mapping of descriptions of the objects to a count:
data.reduce((sum, i) => {
i_str = JSON.stringify(i); // objects can't be keys
sum[i_str] = sum[i_str] ? sum[i_str]+1 : 1;
return sum;
}, {});
A simple solution based on Array#reduce would be as detailed below:
const data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const summary = data.reduce((frequencySummary, item) => {
/* Find a match for current item in current list of frequency summaries */
const itemMatch = frequencySummary.find(i => i.x === item.x && i.y === item.y)
if(!itemMatch) {
/* If no match found, add a new item with inital frequency of 1 to the result */
frequencySummary.push({ ...item, f : 1 });
}
else {
/* If match found, increment the frequency count of that match */
itemMatch.f ++;
}
return frequencySummary;
}, []);
console.log(summary)
I know using reduce is probably better, but I tend to use forEach and findIndex for better readability.
var data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
var summary = [];
data.forEach(function(d){
var idx = summary.findIndex(function(i){
return i.x === d.x && i.y === d.y;
});
if(idx < 0){
var sum = Object.assign({}, d);
sum.f = 1;
summary.push(sum);
} else {
summary[idx].f = summary[idx].f + 1;
}
});
console.log(summary);
Create nested objects. The outer object uses x values as keys, the nested object contains y values as keys, and the values are the frequencies.
data = [
{ x: 1, y: 1 },
{ x: 2, y: 2 },
{ x: 3, y: 3 },
{ x: 2, y: 2 },
{ x: 1, y: 1 },
{ x: 1, y: 2 },
{ x: 1, y: 1 }
];
const nested = data.reduce((a, {x, y}) => {
a[x] = a[x] || {};
a[x][y] = a[x][y] ? a[x][y] + 1 : 1
return a;
}, {});
const summary = [];
Object.keys(nested).forEach(x => Object.keys(nested[x]).forEach(y => summary.push({x, y, f: nested[x][y]})));
console.log(summary);
You can use reduce and Map, club the use x and y as key, on every iteration check if the same key is already present on Map than just increase f count by 1 if not than set it to 1
const data = [{ x: 1, y: 1 },{ x: 2, y: 2 },{ x: 3, y: 3 },{ x: 2, y: 2 },{ x: 1, y: 1 },{ x: 1, y: 2 },{ x: 1, y: 1 }];
const countObj = data.reduce((a, obj) => {
const objString = obj.x + '_' + obj.y;
let value = a.get(objString) || obj
let f = value && value.f || 0
a.set(objString, { ...value, f: f+1 })
return a;
}, new Map());
console.log([...countObj.values()]);
I have an array of point objects:
const points = [ {x: 0, y: 0 }, { x: 1, y: 1}, ...]
I want to convert them to arrays of x and y:
const x = [0, 1, ...];
const y = [0, 1, ...];
I could use 2 maps:
const x = points.map(v => v.x);
const y = points.map(v => v.y);
But that needs 2 iterations over the array. I can do a loop:
const x = [];
const y = [];
for (let i = 0; i < points.length; ++i) {
const pt = points[i];
x.push(pt.x);
y.push(pt.y);
}
That seems overly verbose and potentially slow (all those push backs).
Is there a better method?
At least you need some pushing to the wanted result sets. This solution uses an object for pushing to the right array.
const
points = [{ x: 0, y: 0 }, { x: 1, y: 1 }],
x = [],
y = [],
values = { x, y };
points.forEach(o => Object.entries(o).forEach(([k, v]) => values[k].push(v)));
console.log(x);
console.log(y);
Basically the same, but with known keys.
const
points = [{ x: 0, y: 0 }, { x: 1, y: 1 }],
x = [],
y = [],
values = { x, y },
keys = Object.keys(values);
points.forEach(o => keys.forEach(k => values[k].push(o[k])));
console.log(x);
console.log(y);
While you can do this reasonably easily, say like this:
const points = [{x: 1, y: 2}, {x: 2, y: 3}, {x: 3, y: 5}, {x: 4, y: 7}, {x: 5, y: 11}]
const {x, y} = points.reduce(
({x, y}, pt) => ({x: [...x, pt.x], y: [...y, pt.y]}),
{x: [], y: []}
)
console.log(x)
console.log(y)
there is the bigger question you need to consider of why you want to do this. The original structure is flexible and useful, and you already have it in memory. The new structures depends upon shared indices, which is often difficult to work with and can easily get out of sync. Is there a strong reason not to use the original structure?
Ok, you can do make something like that :
const points = [ {x: 0, y: 0 }, { x: 1, y: 1}];
const t = points.map(item => {
return Object.values(item);
})
const x = t[0];
const y = t[1];
console.log(x,y);
var x= [], y = [], points = [{ x: 0, y: 0 }, { x: 1, y: 10 }];
points.forEach(({x: x1, y: y1}) => (x.push(x1), y.push(y1)))
console.log(x, y)
You can use array#reduce to accumulate result in a multi-dimensional array.
const points = [ {x: 0, y: 0 }, { x: 1, y: 1}],
[x,y] = points.reduce((r,o) => {
Object.values(o).forEach((v,i) => r[i].push(v));
return r;
},[[],[]]);
console.log(x);
console.log(y);
i have an array, filled with arrays, each containing multiple objects. I want to see if my objects exists in there
Ive made a jsfiddle to keep it simple - https://jsfiddle.net/rgnoz31y/1/
Or if you want to just see my code, its below:
blackChains = [];
blackChains.push([{x: 1, y: 2}, {x: 1, y: 3}]);
blackChains.push([{x: 3, y: 4}, {x:4, y: 4}, {x:5, y: 4}]);
currentPiece = {x: 1, y: 3};
const isInChain = blackChains.map(g => g[{}]).includes(currentPiece);
console.log(isInChain);
It currently returns false, when it should be true
As you are using Arrow functions, I assumed you are using ES6.
Using Spreads, I can create an array of all the Array Items https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator
acc.push(...item);
includes would usually work, however it only works on call by reference, not call by value e.g. this would have failed:
console.log([{x: 1, y: 2}, {x: 1, y: 3}].includes(currentPiece));
Some returns true, if at least one of the items matches the condition. By changing the Item and Search Element into a JSON String, we can check by Value.
blackChains = [];
blackChains.push([{x: 1, y: 2}, {x: 1, y: 3}]);
blackChains.push([{x: 3, y: 4}, {x:4, y: 4}, {x:5, y: 4}]);
currentPiece = {x: 1, y: 3};
const isInChain = blackChains.reduce((acc, item) => {
acc.push(...item);
return acc;
}, []).some(item => JSON.stringify(item) === JSON.stringify(currentPiece));
console.log(isInChain);
As commented before,
g[{}] will return undefined. It is interpreted as g["Object object"]
blackChains.map(g => g[{}]) will return an array of length n with all as undefined.
You can use recursion to loop over r nested arrays and stop it when you get Objects.
var blackChains = [];
blackChains.push([{x: 1, y: 2}, {x: 1, y: 3}]);
blackChains.push([{x: 3, y: 4}, {x:4, y: 4}, {x:5, y: 4}]);
var currentPiece = { x: 1, y: 3 };
function searchObjInArray(arr, search) {
if (Array.isArray(arr)) {
return arr.some(function(item) {
return searchObjInArray(item, search)
})
} else if (typeof arr === "object") {
var valid = true;
for (var k in search) {
valid = valid && search[k] === arr[k];
if (!valid) break;
}
return valid;
}
}
var isInChain = searchObjInArray(blackChains, currentPiece)
console.log("isInChain: ", isInChain);
currentPiece.y = 4;
isInChain = searchObjInArray(blackChains, currentPiece)
console.log("isInChain: ", isInChain);
Simply Try with Array#filter() and Array#find() used find the match with c Array
function check(c){
blackChains = [];
blackChains.push([{x: 1, y: 2}, {x: 1, y: 3}]);
blackChains.push([{x: 3, y: 4}, {x:4, y: 4}, {x:5, y: 4}]);
return blackChains.filter(a=> a.find(a=> a.x == c.x && a.y == c.y ))[0] ? true : false;
}
console.log(check({x: 1, y:3}))
console.log(check({x: 1, y:31}))
Replace with:
const isInChain = blackChains.findIndex(
i => i.findIndex(a => a.x === currentPiece.x && a.y === currentPiece.y) > -1) > -1;
You need to check key-value equality explicitly as shown below:
blackChains = [];
blackChains.push([{x: 1, y: 2}, {x: 1, y: 3}]);
blackChains.push([{x: 3, y: 4}, {x:4, y: 4}, {x:5, y: 4}]);
currentPiece = {x: 1, y: 3};
const isInChain = blackChains.map(bc => !!bc.find(o => // return true a false for each index
Object.keys(o).every(key => o[key] === currentPiece[key])));
console.log(isInChain);
I have an array like this.
var nodes = [{ID:"101", x:100, y:200}
,{ID:"102", x:200, y:200}
,{ID:"103", x:300, y:300}
,{ID:"104", x:200, y:300}];
I'd like to have a function which takes node's ID as input and return its (x,y).
For example, the function coordinates(103)should read the array (nodes) and return x = 300, y = 300 when it's called. Any pointer is appreciated. Thanks :)
This is what I have so far. It works but I'd like to know neater and tidier methods.
function coordinates(id){
for (var i=0 in nodes){
if(nodes[i].ID == id){
return { x: nodes[i].x, y: nodes[i].y};
}
}
}
console.log(coordinates(102));
basically you're looking at something like this
var f = function(id){
var match = nodes.filter(function(d){
return d.ID === id;
})
return match && match.length && {x: match[0].x, y:match[0].y}
|| {x: undefined, y: undefined};
};
then f('101') outputs {x: 100, y:200} and if cannot find a match then it will output {x: undefined, y: undefined}
You can use .filter, like so
var nodes = [{
ID: "101",
x: 100,
y: 200
}, {
ID: "102",
x: 200,
y: 200
}, {
ID: "103",
x: 300,
y: 300
}, {
ID: "104",
x: 200,
y: 300
}];
function coordinates(nodes, id) {
var result = nodes.filter(function (el) {
return +el.ID === id;
});
if (result && result.length) {
result = result[0];
return {
x: result.x,
y: result.y
};
}
return null;
}
console.log(coordinates(nodes, 103));
See comments inline:
Demo
var nodes = [{
ID: "101",
x: 100,
y: 200
}, {
ID: "102",
x: 200,
y: 200
}, {
ID: "103",
x: 300,
y: 300
}, {
ID: "104",
x: 200,
y: 300
}];
var noOfCord = nodes.length;
var coordinates = function(id) {
for (var i = 0; i < noOfCord; i++) {
if (nodes[i].ID == id) {
return {
x: nodes[i].x,
y: nodes[i].y
};
}
}
}
document.write(coordinates(103).x + ', ' + coordinates(103).y);
Using array filter, Try:
function coordinates(id){
return nodes.filter(function(e){ return e.ID == id })[0]
}
var nodes=[{ID:"101",x:100,y:200},{ID:"102",x:200,y:200},{ID:"103",x:300,y:300},{ID:"104",x:200,y:300}];
var result = coordinates("103");
document.write("<pre>" + JSON.stringify(result, null, 3));
Brilliant solutions with concrete JavaScript have already been proposed by people here. So I propose another alternative using underscore.js, just in case you're curious.
function coordinates(id){
var n = _.findWhere(nodes, {ID: id});
return {x: n.x, y: n.y }
}