So I hacked this code from somewhere else, but have an object variable to store x,y values in an array every time a hex is clicked, defined with two functions, one to add an element and one to remove the last element
var objMoves = {
length: 0,
addElem: function addElem(elem) {
// obj.length is automatically incremented
// every time an element is added.
[].push.call(this, elem);
},
removeElem: function removeElem(last) {
// this removes the last item in the array
[].splice.call(this,last, 1);
}
};
I call it like this:
objMoves.addElem({ x: hexX, y: hexY });
Result if I dump the objMoves into the console log is "{"0":{"x":2,"y":1},"length":1}"
However, what I really want is something like
objMoves.addElem({ x: hexX, y: hexY },stackID:"abcdef");
So the result would be something like
{stackId:"abcdef",moves:[{"x":2,"y":1},{"x":3,"y":4}]}
{stackId:"xyz",moves:[{"x":5,"y":2},{"x":6,"y":2},{"x":7,"y":2}]}
etc, where the inner array gets added to for a given stackID. I think I need to nest the objects?
push() is for arrays, not objects, so use the right data structure.
var objMoves = [];
// ...
data[0] = { "": "", "": "" };
data[1] = { ....};
// ...
var tempData = [];
for ( var index=0; index<data.length; index++ ) {
if ( data[index].objMoves ) {
tempData.push( data );
}
}
data = tempData;
or deal with it like it is an object. while Objects does not support push property, you can save it as well using the index as key.
It sounds like what you're looking for is something like this:
var objMoves = {
addElem: function(id, elem) {
var obj = this[id] || {
stackId: id,
moves: []
};
obj.moves.push(elem);
this[id] = obj;
},
removeElem: function(id, last) {
this[id].moves.splice(last, 1);
}
}
objMoves.addElem("abcdef", {x: 123, y: 456});
objMoves.addElem("xyz", {x: 1, y: 2});
objMoves.addElem("abcdef", {x: 100, y: 50});
console.log(objMoves);
The functions take a stack ID as a parameter, so they can use that as a key into the object to find the sub-object with that ID. The moves are stored in an array in that sub-object.
I was a little bit fascinated from the code, that you have taken from the MDN JavaScript reference site. It shows us how we could use an object as an array. And so I wrote some functions to do it.
Solution with all, what you need:
var objMoves =
{
// objMoves.length is automatically incremented every time an element is added
length: 0,
//add an object to new(with stackId) or given(by stackId) array element
addElem: function(object, stackId)
{
var index = this.getElemIndex(stackId);
if(index > -1)
this[index].moves.push(object);
else
[].push.call(this, {stackId: stackId, moves: [object]})
},
//remove the array element on lastElemIndex
removeElem: function(lastElemIndex)
{
[].splice.call(this, lastElemIndex, 1)
},
//remove the object on lastElemIndex from the array element with stackId
removeMovesElem: function(stackId, lastElemIndex)
{
var index = this.getElemIndex(stackId);
if(index > -1)
this[index].moves.splice(lastElemIndex, 1)
},
//get the array element index by stackId
getElemIndex: function(stackId)
{
for(var i = this.length; i--;)
if(this[i].stackId == stackId)
return i
return -1
}
};
//we check functions:
objMoves.addElem({x: 2, y: 1}, 'abcdef');
objMoves.addElem({x: 3, y: 4}, 'abcdef');
objMoves.addElem({x: 5, y: 2}, 'xyz');
objMoves.addElem({x: 6, y: 2}, 'xyz');
objMoves.addElem({x: 7, y: 2}, 'xyz');
console.log(JSON.stringify(objMoves, null, '\t'));
console.log('===========================');
var index = objMoves.getElemIndex('abcdef');
objMoves.removeElem(index);
console.log(JSON.stringify(objMoves, null, '\t'));
console.log('===========================');
objMoves.removeMovesElem('xyz', 1);
console.log(JSON.stringify(objMoves, null, '\t'));
Related
I have an object in js: {x: 0, y: 0}, and I want to increment both x and y by one. Now, this isn't a big object, so I can just use placeholder.x and placeholder.y, not very hard. But if I wanted to add more things to the object, the code would get very long and repetitive, so is there a shorter way to apply functions or math to everything in an object?
You can use Object.keys to get an iterable Array of the Object's keys, then use that to loop each and perform the functions you desire:
const myObject = {x: 1, y: 1};
for (key of Object.keys(myObject)) {
myObject[key]++;
}
console.dir(myObject);
In production-grade code, you should check to ensure that the data you're attempting to modify is numerically-typed so you don't end up inadvertently attempting to increment a non-numeric value:
const myObject = {x: 1, y: 1};
for (key of Object.keys(myObject)) {
if (typeof myObject[key] === 'number') myObject[key]++;
}
console.dir(myObject);
You can loop on an object by using Object.keys():
const p1 = {
x: 0,
y: 0
}
function translateXYbyN(p, n) {
Object.keys(p).forEach((c) => p[c] = p[c] + n);
}
console.log(p1);
translateXYbyN(p1, 1);
console.log(p1);
Also, you can also provide a key map in order to manipulate only the wanted keys:
const p2 = {
x: 0,
y: 0,
name: "point"
}
function translateXYbyN(p, n, mask) {
Object.keys(p).forEach((c) => {
if(mask.includes(c))
p[c] = p[c] + n
});
}
console.log(p2);
translateXYbyN(p2, 1, ['x','y']);
console.log(p2);
you can make a function:
const incObj = o => { ++o.x; ++o.y }
const placeholder = {x: 1, y: 1}
incObj( placeholder )
console.log( placeholder )
I have mapsOrder array and mapsData array of objects:
let mapsOrder = [1,2,1,3];
let mapData = [
{
id: 1,
gates: [
{
toId: 2,
coords: {
x: 2,
y: 42
}
},
{
toId: 3,
coords: {
x: 9,
y: 4
}
}
]
},
{
id: 2,
gates: [
{
toId: 1,
coords: {
x: 6,
y: 5
}
}
]
},
{
id: 3,
gates: [
{
toId: 1,
coords: {
x: 2,
y: 1
}
}
]
}
]
What I want to achieve is in loop basing on mapsOrder where mapsOrder array values are ids in mapData, designate gates to next map.
So we have loop that iterate 4 times and when:
loop index is 1 current map is 1 next map is 2 and gates to next are coords: { x: 2, y: 42 }
loop index is 2 current map is 2 next map is 1 and gates to next are coords: { x: 6, y: 5 }
loop index is 3 current map is 1 next map is 3 and gates to next are coords: { x: 9, y: 4 }
loop index is 4 current map is 3 next map is 1 and gates to next are coords: { x: 2, y: 1 }
last loop iteration see next map as first of mapsOrder array. I tried to do it myself by first determineting the id of next map like so:
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapData[mapsOrder[i] - 1].id;
if(i === mapsOrder.length - 1) {
nextMap = mapData[0].id
} else {
nextMapId = mapData[mapsOrder[i]].id;
}
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId)
console.log('break-----')
}
but this console incorrect ids, demo
If you don't care about the original array then just use shift to get the next gate (shift will remove the gate from the array thus the next gate will be available when the object is encountered again). Use find to find the object from the array:
let result = mapsOrder.map(id =>
mapData.find(o => o.id == id).gates.shift().coords
);
You may want to check if find actually finds something and the gates array contains something before using shift, here is a safer way:
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
if(obj && obj.gates.length) { // if we found an object with the same id and that object still have gates
return obj.gates.shift().coords; // return the coords of the first gate and remove the gate from the array
} // otherwise, throw an error or something
});
No altering:
Instead of using shift from the previous example, we'll just use an object to track the gate index from the gates array:
let nextGateIndex = Object.create(null); // create a prototypeless object to track the next gate index for each object
let result = mapsOrder.map(id => {
let obj = mapData.find(o => o.id == id);
let index;
if(nextGateIndex[id] == undefined) {
index = 0;
} else {
index = nextGateIndex[id] + 1;
}
nextGateIndex[id] = index;
if(obj && index < obj.gates.length) {
return obj.gates[index].coords;
} // throw error or something
});
If follow your description your loop should look like. Seems that you wand to use id and toId but using array indexes. It can be a good idea to replace arrays with objects.
Demo
for(let i = 0; i < mapsOrder.length; i++) {
let nextMap;
let currentMapId = mapsOrder[i];
if(i === mapsOrder.length - 1) {
nextMapId = mapsOrder[0]
} else {
nextMapId = mapsOrder[i + 1];
}
let filteredMapData = mapData.filter(f => f.id == currentMapId);
let filteredGates = filteredMapData.length > 0 ? filteredMapData[0].gates.filter(f => f.toId == nextMapId) : [];
console.log('Current map is: ', currentMapId, 'and the next map id is:', nextMapId, 'gates:', filteredGates.length == 0 ? "no gates": filteredGates[0].coords)
console.log('break----')
}
I would recommend the filter() function for javascript arrays as it is super quick. This function will return an array filled with items from original matching some criteria (in this case, objects having the desired id).
for (let i = 0; i < mapsOrder.length; i++) {
console.log(mapData.filter(mapDataItem => mapDataItem.id === mapsOrder[i]))
}
I want to create such array in loop
dataset: [
{
x: 0,
y: 0,
},
{
x: 1,
y: 0.993,
}
]
But this way is not correct.
var array = new Array(10);
for (var i = 0; i < 5; ++i) {
array[i].x = 1;
array[i].y = 2;
}
How I can initialize in correct way?
The comments made by SLaks and squint are correct, so this answer is more of an explanation of why your code isn't working like you think it should, and an example of what you could do instead.
You created an array with room to hold 10 things but you didn't specify what those things were and so nothing is contained in the array.
var array = new Array(10);
you can visualize your array like this:
array = [undefined, undefined, undefined, undefined,...
The array you created was just a container for 10 things not yet defined. When you tried to assign the 'x' and 'y' properties of the array elements, you were were trying to operate on something that did not exist. To get what you want, I suggest creating an object that has the properties you want, with initial values, and then use your loop to add the number of elements you want.
var array = [];
var arrayObject = {x:0,y:0};
for(i=0; i < 10; i++){
array.push(arrayObject);
}
You can do this job in one assignment line as follows;
var dataSet = (new Array(10)).fill("initial y value").reduce((p,c,i) => p.concat({x:i,y:c}),[]);
console.log(dataSet);
I just couldn't figure what y values you would like to have so inserted the initial values of the array. Change them the way you like later. I hope it helps.
Replace the new Array(10) with
var array = Array.apply( {}, { length: 10 } ).map( function() { return {} });
new Array(10) is creating an array like
[ undefined, undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined]
So you are trying to assign x on undefined
If you tried
new Array(10).map(function(){ return {}; }) it will not work either.
An es6 way to do it would be
Array.from(new Array(10), () => { return { x: 1, y: 2 }; })
In JavaScript the Array acts different than in static-typed languages, so there's no need to initialize it with fixed length.
For ECMAScript 6 specification and later:
var points = [].fill.call({ length: 5 }, {x: 1, y: 1});
It produces
[{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1},
{x: 1, y: 1}]
To ensure old browsers' support use for loop:
var points = [{x: 1, y: 1}];
for (var i = 0; i < 5; i++) points.push(points[0]);
I'm building a filtering method for Mixitup and I need to be able to correctly filter against x selected parameters. (The plugin here isn't important, its how I get my correct end object)
I currently have an object, that for each unique search is sending a unique key, and its matching objects (obtained via the filter method) into my filtering function.
This is where I am lost.
I need to be able to loop over my object, and it's associated key => values (objects), and fetch out only the objects that exist in each.
For example, (instead of numbers I have jQuery objects)
var filter = {
x : {1,3,5,6},
y : {1,4,7,8},
z : {1,9}
}
Based on the above example, the only returned object would be - 1 (as its the only one to exist in all three keys.
Any help would be greatly appreciated!
A short approach with Array.reduce and Array.filter:
Basically it starts with the first array as start value for the reduce. Then the result set is filtered by the lookup of the indices, and if found, the value remains, otherwise the value is skipped. This continues until the object has no more property.
var filter = {
x: [1, 3, 5, 6],
y: [1, 4, 7, 8],
z: [1, 9]
};
var filtered = Object.keys(filter).reduce(function (r, a, i) {
return i ? r.filter(function (b) {
return ~filter[a].indexOf(b);
}) : filter[a];
}, []);
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
Bonus with objects, returns the common key/s:
var filter = {
x: { a: 'ah', c: 'ce', e: 'eh', f: 'ef' },
y: { a: 'ah', d: 'de', g: 'ge', h: 'ha' },
z: { a: 'ah', i: 'ie' }
};
var filtered = Object.keys(filter).reduce(function (r, a, i) {
return i ? r.filter(function (b) {
return b in filter[a];
}) : Object.keys(filter[a]);
}, []);
document.write('<pre>' + JSON.stringify(filtered, 0, 4) + '</pre>');
So, I would do this in 2 parts:
1 Find the common items
2 Extract the common items
var filter = {
x : [1,3,5,6],
y : [1,4,7,8],
z : [1,9]
}
// Find the common
var common;
Object.keys(filter).forEach(function (k) {
if (common) {
for (var i=0; i<common.length; i++) {
// Check if the common value exists on the key
if (filter[k].indexOf(common[i]) === -1) {
// If it does not, it is not common
common.splice(i, 1);
i--;
}
}
} else {
// On the first item, we assume all are common
common = filter[k]
}
})
// Additional processing can take place to extract the data you need here
//Should print "[1]"
console.log(common)
This is a solution: Here I used the same structure of your data, without converting it to arrays
HTML
<p id="result"></p>
JavaScript
var value = 'value',
filter = {
x : {1:value,3:value,5:value,6:value,9:value},
y : {1:value,4:value,7:value,8:value,9:value},
z : {1:value,9:value}
};
function getCommon(data) {
var subObjects = [],
common = [];
if(typeof data === 'object'){
// collect all sub-keys into an array
Object.keys(data).forEach(function(key){
if(typeof data[key] === 'object') {
subObjects = subObjects.concat(Object.keys(data[key]));
}
});
// get the common keys
Object.keys(data[Object.keys(data)[0]]).forEach(function(subKey){
if(getCount(subObjects, subKey) === Object.keys(data).length) {
common.push(subKey);
}
});
function getCount(data, target){
return data.filter(function(item){
return item === target;
}).length;
}
return common;
}
}
document.getElementById('result').innerHTML = getCommon(filter);
JSFiddle: http://jsfiddle.net/LeoAref/bbnbfck7/
I've been using underscore.js for a week now and I really like it.
However, now I want to replace a single element in collection (vm.lists) with another element.
I tried doing the following:
_.each(vm.lists, function (el) {
if (el.id == list.id)
el = list;
});
and:
var l = _.findWhere(vm.lists, { id: list.id });
l = list;
But none of them changes the actual item in the collection. How would I do this properly?
You got pretty close. The issue is that you are only replacing the local variables (el and l) pointing to the value, and this does not modify the initial list. A simpler example:
var list = [1, 2, 3];
var v = list[1];
v = 7; // v will now point to a new value, but list will stay intact [1, 2, 3]
Same with objects:
var olist = [{id: 1, v: 2}, {id: 4, v: 6}];
var obj = olist[0];
obj = {id: 8, v: 10}; // changes what obj points to, but does not affect olist[0]
var obj2 = olist[0];
obj2.v = 777; // olist[0] still points to the same object but the object is modified
olist[0] = {id: 8, v: 10}; // changes what olist[0] points to
So basically you have two options:
a) change vm.lists[index] so that it points to a new object. You would need to get the index of the object in the list and do vm.lists[index] = newObject;. Note that some underscore predicates also provide you the index _.each(list, function (el, index) {});.
b) change the object that vm.lists[index] points to, but you would need to copy fields manually. For example, if your object is represented as {id: id, values: [1, 2, 3], anotherField: data}, you could copy the fields:
//el.id = list.id; // don't need this as you were searching by id
el.values = list.values;
el.anotherField = list.anotherField;
IMO the first option would be cleaner.
there is no need for underscore here:
for (var i = 0, l = vm.lists.length; i < l; i++)
{
var el = vm.lists[i];
if (el.id == list.id) {
vm.lists[i] = list;
break; // stop the for loop
}
}