From a Single JavaScriptObject to Multiple JavaScriptObjects - javascript

I have the following javaScript object
var stats = [
{x: 10, y: 300, clr:'blue'},
{x: 16, y: 600, clr:'blue'},
{x: 26, y: 300, clr:'yellow'},
{x: 36, y: 200, clr:'yellow'},
{x: 46, y: 700, clr:'green'},
{x: 56, y: 100, clr:'green'},
];
How could I able to get the following objects? Each object seperated based on clr property The main key points are to separate and append the last object of the previous object to the first object for new object.This step is required to connect lines between.
var stats1 = [
{x:10, y:300, clr:'blue'},
{x:16, y:600, clr:'blue'},
];
var stats2 = [
{x:16, y:600, clr:'yellow'},
{x:26, y:300, clr:'yellow'},
{x:36, y:200, clr:'yellow'}
];
var stats3 = [
{x:36, y:200, clr:'green'},
{x:46, y:700, clr:'green'},
{x:56, y:100, clr:'green'}
];

Another solution as you want last object to be 1st of the next array object:
var stats = [
{x: 10, y: 300, clr:'blue'},
{x: 16, y: 600, clr:'blue'},
{x: 26, y: 300, clr:'yellow'},
{x: 36, y: 200, clr:'yellow'},
{x: 46, y: 700, clr:'green'},
{x: 56, y: 100, clr:'green'},
];
function groupBy( array ,prop, f )
{
var groups = {};
///grouping & sorting
array.forEach( function( o )
{
var group = JSON.stringify( f(o) );
groups[group] = groups[group] || [];
groups[group].push( o );
});
var last_prop = "" ;
var last_elm = {} ;
for(var key in groups )
{
if(last_prop !== "")
{
last_elm[prop] = groups[key][0][prop] ;
groups[key].unshift(last_elm);
}
last_prop = key;
last_elm = (JSON.parse(JSON.stringify(groups[key][groups[key].length - 1])));
}
return Object.keys(groups).map( function( group )
{
return groups[group];
});
}
var result = groupBy(stats, "clr", function(item)
{
return [item.clr];
});
console.log(result);
document.write(JSON.stringify(result));
if it doesn't work try jsbin version http://jsbin.com/tudahibefo/1/

var stats = [
{x: 10, y: 300, clr:'blue'},
{x: 16, y: 600, clr:'blue'},
{x: 26, y: 300, clr:'yellow'},
{x: 36, y: 200, clr:'yellow'},
{x: 46, y: 700, clr:'green'},
{x: 56, y: 100, clr:'green'},
];
var stats1 = []; // blue
var stats2 = []; // yellow
var stats3 = []; // green
var last_item = null;
for (var i = 0; i < stats.length; i++) {
var cur_item = stats[i];
switch (cur_item.clr) {
case "blue":
target = stats1;
break;
case "yellow":
target = stats2;
break;
case "green":
target = stats3;
break;
}
if (last_item && last_item.clr != cur_item.clr) {
// Push last item of previous colow onto new color
target.push({
x: last_item.x,
y: last_item.y,
clr: cur_item.clr
});
}
target.push(cur_item);
last_item = cur_item;
}
document.getElementById("output").innerHTML = JSON.stringify(stats1) + "<br>" + JSON.stringify(stats2) + "<br>" + JSON.stringify(stats3);
<div id="output"></div>

Use filter to obtain three separate arrays:
stats1 = stats.filter(function(x){return x.clr=="blue"});
stats2 = stats.filter(function(x){return x.clr=="yellow"});
stats3 = stats.filter(function(x){return x.clr=="green"});
then add the last element of the previous array to the next one with splice
stats2.splice(0,0,stats1[stats1.length-1]);
stats3.splice(0,0,stats2[stats2.length-1]);
then change the color of the first element of arrays stats2 and stats3
stats2[0].clr="yellow";
stats3[0].clr="green";

UPDATE: I've updated the solution to have the ability to connect last elements to form a link
I present a generic solution that allows you to split the array irrespective of which colors are part of the stats array.
var stats = [
{x: 10, y: 300, clr:'blue'},
{x: 16, y: 600, clr:'blue'},
{x: 26, y: 300, clr:'yellow'},
{x: 36, y: 200, clr:'yellow'},
{x: 46, y: 700, clr:'green'},
{x: 56, y: 100, clr:'green'},
];
// the function receives the property with which you want to divide
// in our case, it is the 'clr' property
// the 'connectLast' boolean flag is used to connect the last link
var divideArrayByProperty = function(arr, property, connectLast) {
var dividedArrays = {};
var key = null;
var lastElement = null;
for (var i = 0; i < arr.length; i++) {
key = arr[i][property];
if (undefined === dividedArrays[key]) {
dividedArrays[key] = [];
if (connectLast === true && i > 0) {
lastElement = JSON.parse(JSON.stringify(arr[i-1]));
lastElement.clr = key;
dividedArrays[key].push(lastElement);
}
}
dividedArrays[key].push(arr[i]);
}
return dividedArrays;
};
var result = divideArrayByProperty(stats, 'clr', true);
document.getElementById("output").innerHTML = JSON.stringify(result);
<div id='output'></div>

Yet another solution.
var stats = [
{x: 10, y: 300, clr:'blue'},
{x: 16, y: 600, clr:'blue'},
{x: 26, y: 300, clr:'yellow'},
{x: 36, y: 200, clr:'yellow'},
{x: 46, y: 700, clr:'green'},
{x: 56, y: 100, clr:'green'},
];
function groupWith(arr, f) {
var result = [], group = [];
arr.forEach(function(elem) {
if(group.length !== 0 && !f(group[group.length - 1], elem)) {
result.push(group);
group = [];
}
group.push(elem);
});
result.push(group);
return result;
}
function getLast(array) {
return array[array.length - 1];
}
function copyObject(obj) {
return JSON.parse(JSON.stringify(obj));
}
var statsGrouped = groupWith(stats, function(a, b) {return a.clr === b.clr});
for(var i = 1; i < statsGrouped.length; i++) {
statsGrouped[i].unshift(copyObject(getLast(statsGrouped[i-1])));
statsGrouped[i][0].clr = statsGrouped[i][1].clr;
}
console.log(statsGrouped);

Related

Get the id of the nearest coordinate object

I am using the answer in the example to find the nearest object coordinate. How do I find the index of the nearest point from var points if e.g. the nearest point is {x: 12, y: 18}, then index = 1?
I want to avoid using an extra indexOf() if that step can be saved.The code is:
var points = [
{x: 10, y: 20},
{x: 12, y: 18},
{x: 20, y: 30},
{x: 5, y: 40},
{x: 100, y: 2}
];
function d(point) {
return Math.pow(point.x, 2) + Math.pow(point.y, 2);
}
var closest = points.slice(1).reduce(function(min, p) {
if (d(p) < min.d) min.point = p;
return min;
}, {point: points[0], d:d(points[0])}).point;
console.log(closest);
You could map the array to an array of objects first that also contain the index:
const points = [
{x: 10, y: 20},
{x: 12, y: 18},
{x: 20, y: 30},
{x: 5, y: 40},
{x: 100, y: 2}
];
const distance = (point) => {
return Math.pow(point.x, 2) + Math.pow(point.y, 2);
}
const result = points
.map((point, index) => ({ point, index }))
.reduce((a, b) => distance(a.point) < distance(b.point) ? a : b);
console.log(result);

How to map a object of objects getting the key and value?

I have map called "marks" that has other maps as fields. I need to do something like a forEach loop (or a map) with this getting the key and the value of each nested map.
This is my data:
"marks" : {
"mark_01": {x: 10, y: 200},
"mark_02": {x: 200, y: 100},
...
"mark_99": {x: 1000, y: 1039}
}
What I am trying to do is:
// This is wrong but represents the main idea
const newMarks = marks.map(mark => {
// Get the mark key "mark_number"
// Change its value (x, y)
// Return an object with the same key and the new manipulated value
})
Any ideas? The resulted data has to look like this:
"marks" : {
"mark_01" : {x: 0, y: 190},
"mark_02" : {x: 190, y: 90},
...
"mark_99" : {x: 990, y: 1029}
}
Below snippet could help you
const { marks } = {
marks: {
mark_01: { x: 10, y: 200, other_if_have: 'a' },
mark_02: { x: 200, y: 100, other_if_have: 'b' },
mark_99: { x: 1000, y: 1039, other_if_have: 'c' },
},
}
const temp = Object.keys(marks).map((mark) => {
const manipulate = ({ x, y }) => ({
x: x - 10,
y: y - 10,
})
return [mark, { ...marks[mark], ...manipulate(marks[mark]) }]
})
const res = { marks: Object.fromEntries(temp) }
console.log(res)
Ref:
Object.keys() doc
Object.entries() doc
Another alternative solution:
const edited = Object.fromEntries(Object.entries(marks).map(
([k,{x,y}]) => [k,{x: x+10, y: y+10}]
));
You can check it here: https://jsfiddle.net/sandro_paganotti/ztw1exb4/13/
Could use a for...in loop:
const marks = {
"mark_01": {
x: 10,
y: 200
},
"mark_02": {
x: 200,
y: 100
},
"mark_99": {
x: 1000,
y: 1039
}
}
console.log("before", marks)
for (let i in marks) {
marks[i].x += 1;
marks[i].y += 1;
}
console.log("after", marks)
Though, you should note:
The problem with a for...in loop is that it iterates through properties in the Prototype chain. When you loop through an object with the for...in loop, you need to check if the property belongs to the object. You can do this with hasOwnProperty.
So to account for this:
const marks = {
"mark_01": {
x: 10,
y: 200
},
"mark_02": {
x: 200,
y: 100
},
"mark_99": {
x: 1000,
y: 1039
}
}
console.log("before", marks)
for (let i in marks) {
if (marks.hasOwnProperty(i)) {
marks[i].x += 1;
marks[i].y += 1;
}
}
console.log("after", marks)
This is a good article to check out for something like this.
if you have undetermined number of properties inside your object you can do nested for like that
let marks = {
"mark_01": {x: 10, y: 200, z: 300, ...},
"mark_02": {x: 200, y: 100, z: 10, ...},
"mark_99": {x: 1000, y: 1039, z: 1200, ...}
}
let newMarks = {}
for (const [key, value] of Object.entries(marks)) {
let newValues = {}
for (const [innerKey, innerValue] of Object.entries(value)) {
newValues[innerKey] = innerValue - 10
}
newMarks[key] = newValues
}
console.log(newMarks);

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))

How to sum property in array of objects, based on a condition

I'm a junior Web Developer, looking for some guidance solving a problem. Please excuse me if I'm missing anything integral, as this my first time posting here.
I have an array of some data returned like so:
[
{x: Date(1234), y: 0}
{x: Date(1235), y: 0}
{x: Date(1236), y: 300}
{x: Date(1237), y: 300}
{x: Date(1238), y: 300}
{x: Date(1239), y: 300}
{x: Date(1240), y: 300}
{x: Date(1241), y: 0}
{x: Date(1242), y: 0}
{x: Date(1243), y: 0}
]
If possible, I'd like to return a new array in which all consecutive 'y' values > 0 are summed. In the new array, the summed value should be associated with the first 'x' value of the summed items, like so:
[
{x: Date(1234), y: 0}
{x: Date(1235), y: 0}
{x: Date(1236), y: 1500}
{x: Date(1241), y: 0}
{x: Date(1242), y: 0}
{x: Date(1243), y: 0}
]
I'm thinking this will likely involve 'reduce,' but I'm a little unsure how to proceed. Any help would be greatly appreciated.
Thanks in advance!
Using reduce, you could do something like this: https://jsbin.com/leladakiza/edit?js,console
var input = [
{x: Date(1234), y: 0},
{x: Date(1235), y: 0},
{x: Date(1236), y: 300},
{x: Date(1237), y: 300},
{x: Date(1238), y: 300},
{x: Date(1239), y: 300},
{x: Date(1240), y: 300},
{x: Date(1241), y: 0},
{x: Date(1242), y: 0},
{x: Date(1243), y: 0},
];
var output = input.reduce(function (acc, val) {
var lastIndex = acc.length - 1;
if (val.y <= 0 || lastIndex < 0 || acc[lastIndex].y <= 0) {
acc.push(val);
} else {
acc[lastIndex].y += val.y;
}
return acc;
}, []);
I think you can use a reduce function like this.
var arr = [{
x: Date(1234),
y: 0
},
{
x: Date(1235),
y: 0
},
{
x: Date(1236),
y: 300
},
{
x: Date(1237),
y: 300
},
{
x: Date(1238),
y: 300
},
{
x: Date(1239),
y: 300
},
{
x: Date(1240),
y: 300
},
{
x: Date(1241),
y: 0
},
{
x: Date(1242),
y: 0
},
{
x: Date(1243),
y: 0
}
];
var yGreaterThanZero = null;
var aggregated = arr.reduce(function(acc, cur) {
if (cur.y > 0) {
if (!yGreaterThanZero) {
acc.push(cur);
yGreaterThanZero = cur;
} else {
yGreaterThanZero.y += cur.y;
}
} else {
acc.push(cur);
}
return acc;
}, []);
console.log(aggregated);
This is a very crude logic. We start recording when a value greater than 0 is obtained and push it at the end of the recording (value less than 0)
var a = [
{x: Date(1234), y: 0},
{x: Date(1235), y: 0},
{x: Date(1236), y: 300},
{x: Date(1237), y: 300},
{x: Date(1238), y: 300},
{x: Date(1239), y: 300},
{x: Date(1240), y: 300},
{x: Date(1241), y: 0},
{x: Date(1242), y: 0},
{x: Date(1243), y: 0},
{x: Date(1244), y: 200},
{x: Date(1245), y: 200},
{x: Date(1246), y: 200},
{x: Date(1247), y: 200},
]
var newA = [];
var recording = false;
var temp = {}
a.forEach(item => {
if (item.y > 0) {
recording = true;
if (temp.y) {
if(!temp.x) temp.x = item.x;
temp.y = temp.y + item.y
} else {
temp = item;
}
} else {
if (recording) newA.push(temp)
recording = false;
temp = {};
newA.push(item);
}
})
if (recording) newA.push(temp)
console.log(newA)

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