I have a JSON array that represents a list of objects (people).
every object (every person) has name attribute, image, and an array of numbers.
Example:
"people":[
{
"name":"nunu",
"image":"image1",
"numbers":{
"total":50,
"vector":[
10,
20,
5,
10,
5
]
}
}
];
My goal is to update all vectors and append some calculation to each vector.
This is what I tried:
this.people = this.people.map((person) => {
return person.numbers.vector.map((num) => {
return Math.round(((num / divider) * 100) / 100);
});
});
The problem is that people is being replaced by the numbers in my vector and I lose the people data.
How I can update the vectors without making nay change to any other data?
Due to .map() specification it creates a new array, to process top-level list use .forEach() instead:
this.people.forEach(person =>
person.numbers.vector = person.numbers.vector.map(num =>
Math.round(((num / divider) * 100) / 100)
);
);
people = people.map((person) => {
person.numbers.vector = person.numbers.vector.map((num) => {
return Math.round(((num / divider) * 100) / 100);
});
return person;
});
You were returning the vector as the value for person, you need to change the value of vector then return person instead.
Try using spread operator to update person object and save all data. For example, calculate total value as sum of nums:
this.people = this.people.map((person) => {
let total = person.numbers.vector.reduce((prevNum, nextNum) => {
return prevNum + nextNum;
});
return {
...person,
numbers: {
...person.numbers,
total
}
}
});
At the same way you can change vector values, for example
If you use the new spread operator and something like Babel this becomes trivial:
const source = {
"people": [{
"name": "nunu",
"image": "image1",
"numbers": {
"total": 50,
"vector": [
10,
20,
5,
10,
5
]
}
}]
};
const newSource = {
...source,
people: source.people.map(person => {
return {
...person,
numbers: {
...person.numbers,
vector: person.numbers.vector.map(n => Math.round(((n / 2) * 100) / 100))
}
}
})
};
Here is more on the spread operator.
As a side note, using the spread operator creates a new object and using map creates a new array. This way you will always have a new object and can't change the old one. Using const with this type of code is also a good practice.
Related
Given an instance v of a class Vector, say v = new Vector(3, 5, 7), is it somehow possible to use the syntax v(k) to call a specific method of the class Vector on v with argument(s) k?
To provide some context, I'd like to be able to use v(k) to call the method getElem(k) on v, which retrieves the k-th vector element of v. For example, v(2) would return 7. In other words, v(k) would act as an alias or shorthand for v.getElem(k).
Of course it would be possible to write a (custom) pre-processor to achieve the above, I just wondered whether there is a built-in way to realise it.
This question was inspired by the syntax of the C++ library Eigen, which allows one to get/set matrix elements in a similar way. It would be lovely to have something like this in JavaScript.
A bit of code to accompany the class mentioned above —
class Vector {
constructor(...vecElems) {
this.vecElems = vecElems;
}
getElem(k) {
return this.vecElems[k];
}
dot(v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.log(v.getElem(2), v.dot(w));
Finally was pointed out a fairly elegant injection-safe way to do it in ES6+ class syntax with a Proxy:
class Vector extends Function {
constructor(...vecElems) {
super();
this.vecElems = vecElems;
return new Proxy(this, {
apply(target, _, args) {
return target.getElem(...args);
}
});
}
getElem(k) {
return this.vecElems[k];
}
dot(v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.log(v.getElem(2), v.dot(w));
console.log(v(2), v.dot(w));
This uses Proxy's handler.apply to wrap a callable object, which is in this case the class instance itself because it extends Function. It has to extend Function for the x(...) call syntax to be valid.
You can make the syntax v[i] return v.getElem(i) too, by subclassing Array itself:
class Vector extends Array {
constructor(...vecElems) {
super(...vecElems);
}
getElem(k) {
return this[k];
}
dot(v) {
return this.reduce((aV, cV, cI) => aV + cV * v[cI], 0);
}
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.log(v.getElem(0), v.getElem(1));
console.log(v[0], v[1]);
console.log(v.dot(w));
Seems what you want is an object-generator (i.e. class) that returns functions that also have you custom properties.
Here's a way you can create such a "class":
function Vector(...vecElems) {
let members = {
vecElems,
getElem: function (k) {
return this.vecElems[k];
},
dot: function (v) {
return this.vecElems.reduce((aV, cV, cI) => aV + cV * v.vecElems[cI], 0);
}
};
return Object.assign(members.getElem.bind(members), members);
}
const v = new Vector(3, 5, 7);
const w = new Vector(4, 6, 8);
console.log(v.getElem(0), v.getElem(1));
console.log(v.dot(w));
console.log(v(0), v(1));
This will be more performant than the ES6-class-syntax with Proxy approach, because member access via Proxy suffers its indirection.
I have an array with objects:
data = [{id:"a", position:{x:10,y:10}, parent:'3a'},{id:"b", position:{x:100,y:100}}]
I am using map method to create new array in which I want to have new prop position but based on exsisting without mutating original value from data array. I know I have to use object assign to prevent from mutating but I can not figure out how to set position basing and not changing original one:
if (e.parent) {
const newObject = Object.assign({}, e, {
position: {
x: (e.position.x -= bbX / 2 - parent.x),
y: (e.position.y -= bbY / 2 - parent.y)
}
});
return newObject;
}
return e;
});
I tried with this one and similar but I still see mutation in original data array when it comes to position
The problem is caused by following lines:
x: (e.position.x -= bbX / 2 - parent.x),
y: (e.position.y -= bbY / 2 - parent.y)
When you use subtraction assignment operator -=, you also assign the result to the left variable which is the orignal data, just subtraction operator - would be fine:
const data = [{id:"a", position:{x:10,y:10}, parent:'3a'},{id:"b", position:{x:100,y:100}}];
const newData = data.reduce((arr, cur) => {
let e = Object.assign({}, cur, {position: {x: (cur.position.x - 5),y: (cur.position.y - 5)}});
arr.push(e);
return arr;
},[]);
console.log("original data:" + JSON.stringify(data));
console.log("new data:" + JSON.stringify(newData));
In typescript, I am trying to assign a random number to a property of each object in an array. I have tried the following code
uniqueItems.forEach(unique => {
unique.humanCode = Math.floor(1000 + Math.random() * 9000).toString();
});
If I console.log inside the forEach loop, I get a different number but when I console.log the array of objects after the forEach, all of the random numbers are the same.
Edit: I had originally created my array of objects by using
for (let i = 0; i < this.quantity; i++) {
this.uniqueItems.push(uniqueItem);
}
This made an array of the same object. This meant that my array was being assigned the last random number. I fixed this by using the spread operator when I created my array.
for (let i = 0; i < this.quantity; i++) {
this.uniqueItems.push({ ...this.uniqueItem });
}
You can use map to iterate over your items and return a new object for each item in the array. This will also ensure you are no updating the object by reference. You should read about mutability in JavaScript. It's an important concept in JS
uniqueItems.map(unique => ({
...unique,
humanCode: Math.floor(1000 + Math.random() * 9000).toString(),
}));
I have tried the below code and it works for me.
let uniqueItems = [{
humanCode: 0,
},
{
humanCode: 0,
},
{
humanCode: 0,
},
]
uniqueItems.forEach(unique => {
unique.humanCode = Math.floor(1000 + Math.random() * 9000).toString();
});
console.log(uniqueItems);
Say we have an array:
var antibiotics = [{
bacteria: "Mycobacterium tuberculosis",
penicillin: 800,
streptomycin: 5,
neomycin: 2,
gram: "negative"
}, {
bacteria: "Salmonella schottmuelleri",
penicillin: 10,
streptomycin: 0.8,
neomycin: 0.09,
gram: "negative"
}, {
bacteria: "Proteus vulgaris",
penicillin: 3,
streptomycin: 0.1,
neomycin: 0.1,
gram: "negative"
}, {
bacteria: "Klebsiella pneumoniae",
penicillin: 850,
gram: "negative"
}];
And we want to find minand max of all numerical properties of objects in array (penicillin, streptomycin and neomycin here) assuming values can be null/absent.
How to aggregate such data from an array of objects in JavaScript?
You could use Array.prototype.map() to pluck the values you require, and then pass as arguments to Math.max() or Math.min().
Math.max.apply(Math, values);
Unfortunately, JS standard library doesn't provide Array.max or a "pluck" (=collect) function, but there are many libraries that do, e.g. underscore:
maxPenicillin = _.max(_(antibiotics).pluck('penicillin')))
If you don't like libraries, these function are easy:
Array.prototype.max = function() {
return this.reduce(function(a, b) {
return a > b ? a : b;
})
}
Array.prototype.pluck = function(prop) {
return this.map(function(x) {
return x[prop];
})
}
maxPenicillin = antibiotics.pluck('penicillin').max()
but really, why would you want to reinvent the wheel? Just use a library.
Upd: if I interpret your comment correctly, you're looking for something like this:
var values = {};
_.each(antibiotics, function(x) {
_.each(x, function(v, k) {
if(!isNaN(v))
values[k] = (values[k] || []).concat([v]);
})
})
var minmax = {};
_.each(values, function(v, k) {
minmax[k] = [_.min(v), _.max(v)]
})
Result:
{"penicillin":[3,850],"streptomycin":[0.1,5],"neomycin":[0.09,2]}
This should do the trick.
http://jsfiddle.net/vgW4v/
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
Array.prototype.min = function() {
return Math.min.apply(null, this);
};
And arrays are populated with values if they exist and simply calculated.
Hope this helps.
This question already has answers here:
Getting a random value from a JavaScript array
(28 answers)
Closed 7 years ago.
var items = Array(523, 3452, 334, 31, ..., 5346);
How do I get random item from items?
var item = items[Math.floor(Math.random()*items.length)];
1. solution: define Array prototype
Array.prototype.random = function () {
return this[Math.floor((Math.random()*this.length))];
}
that will work on inline arrays
[2,3,5].random()
and of course predefined arrays
var list = [2,3,5]
list.random()
2. solution: define custom function that accepts list and returns element
function get_random (list) {
return list[Math.floor((Math.random()*list.length))];
}
get_random([2,3,5])
Use underscore (or loDash :)):
var randomArray = [
'#cc0000','#00cc00', '#0000cc'
];
// use _.sample
var randomElement = _.sample(randomArray);
// manually use _.random
var randomElement = randomArray[_.random(randomArray.length-1)];
Or to shuffle an entire array:
// use underscore's shuffle function
var firstRandomElement = _.shuffle(randomArray)[0];
If you really must use jQuery to solve this problem (NB: you shouldn't):
(function($) {
$.rand = function(arg) {
if ($.isArray(arg)) {
return arg[$.rand(arg.length)];
} else if (typeof arg === "number") {
return Math.floor(Math.random() * arg);
} else {
return 4; // chosen by fair dice roll
}
};
})(jQuery);
var items = [523, 3452, 334, 31, ..., 5346];
var item = jQuery.rand(items);
This plugin will return a random element if given an array, or a value from [0 .. n) given a number, or given anything else, a guaranteed random value!
For extra fun, the array return is generated by calling the function recursively based on the array's length :)
Working demo at http://jsfiddle.net/2eyQX/
Here's yet another way:
function rand(items) {
// "~~" for a closest "int"
return items[~~(items.length * Math.random())];
}
Or as recommended below by #1248177:
function rand(items) {
// "|" for a kinda "int div"
return items[items.length * Math.random() | 0];
}
var random = items[Math.floor(Math.random()*items.length)]
jQuery is JavaScript! It's just a JavaScript framework. So to find a random item, just use plain old JavaScript, for example,
var randomItem = items[Math.floor(Math.random()*items.length)]
// 1. Random shuffle items
items.sort(function() {return 0.5 - Math.random()})
// 2. Get first item
var item = items[0]
Shorter:
var item = items.sort(function() {return 0.5 - Math.random()})[0];
Even shoter (by José dB.):
let item = items.sort(() => 0.5 - Math.random())[0];
var rndval=items[Math.floor(Math.random()*items.length)];
var items = Array(523,3452,334,31,...5346);
function rand(min, max) {
var offset = min;
var range = (max - min) + 1;
var randomNumber = Math.floor( Math.random() * range) + offset;
return randomNumber;
}
randomNumber = rand(0, items.length - 1);
randomItem = items[randomNumber];
credit:
Javascript Function: Random Number Generator
If you are using node.js, you can use unique-random-array. It simply picks something random from an array.
An alternate way would be to add a method to the Array prototype:
Array.prototype.random = function (length) {
return this[Math.floor((Math.random()*length))];
}
var teams = ['patriots', 'colts', 'jets', 'texans', 'ravens', 'broncos']
var chosen_team = teams.random(teams.length)
alert(chosen_team)
const ArrayRandomModule = {
// get random item from array
random: function (array) {
return array[Math.random() * array.length | 0];
},
// [mutate]: extract from given array a random item
pick: function (array, i) {
return array.splice(i >= 0 ? i : Math.random() * array.length | 0, 1)[0];
},
// [mutate]: shuffle the given array
shuffle: function (array) {
for (var i = array.length; i > 0; --i)
array.push(array.splice(Math.random() * i | 0, 1)[0]);
return array;
}
}