How to access a key/value pair with a variable? - javascript

I am looking to access a key/value pair dynamically using the a variable. I can get the value I'm looking for using topics.social.color, but the topic will change based on the user's selection. How can I dynamically get the topic color using a variable for the topic?
let topic = 'social';
const topics = {
social: {
color: 'red'
},
emotional: {
color: 'blue'
},
physical: {
color: 'green'
}
};
console.log(topics.topic.color); // this does not work
CodePen: https://codepen.io/m-use/pen/XWJKBMB

this should do the trick
topics[topic].color
let topic = 'social';
const topics = {
social: {
color: 'red'
},
emotional: {
color: 'blue'
},
physical: {
color: 'green'
}
};
console.log(topics[topic].color);
Further Info

Related

How can I group an array of objects by key and create another object inside

Does anyone know how to group an array of objects by an object key and then create a new array of objects based on the grouping? For example, I have an array of Build objects as below and I want to group by products and create another object of colors and price based on that.
build = [
{
'Product': 'Cabinets',
'Color': 'Blue',
},
{
'Product': 'CounterTop',
'Color': 'White',
},
{
'Product': 'Cabinets',
'Color': 'Yellow',
},
{
'Product': 'Cabinets',
'Color': 'Yellow',
}
]
And I want it like this
[
{
'Product':'Cabinet',
'color' : { 'Blue','Yellow' }
},
{
'Product':'CounterTop',
'color' : { 'White' }
}
]
I wrote a code to archive it but I am not getting the result as expected.
build.forEach(pr => {
if (pr.Product in result) {
result[pr['Product']]['Color'] = pr['Color'];
}
else {
result[pr['Product']] = {
'Product': pr['Product'],
'Color': pr['Color']
}
}
});
Above code returns
[
{
'Product':'Cabinet',
'color' : 'Yellow'
},
{
'Product':'CounterTop',
'color' : 'White'
}
]
Expecting 'color' : { 'Blue','Yellow' } in your output is wrong. Objects are key-value pairs of data.
Instead, you want color to be an array. I adjusted your code:
build.forEach(pr => {
if (pr.Product in result) {
result[pr['Product']]['Color'].push(pr['Color']);
} else {
result[pr['Product']] = {
'Product': pr['Product'],
'Color': [pr['Color']]
}
}
});
Now think about how you can prevent duplicate values in the array. #Lissy93's answer helps with that by using findIndex.
Here's a working version. Hope it helps :)
const builds = [
{ 'Product': 'Cabinets', 'Color': 'Blue' },
{ 'Product': 'CounterTop', 'Color': 'White' },
{ 'Product': 'Cabinets', 'Color': 'Yellow' },
{ 'Product': 'Cabinets', 'Color': 'Yellow' }
];
const results = [];
builds.forEach((build) => {
const index = results.findIndex((b) => b.Product === build.Product);
if (index === -1) {
results.push({Product: build.Product, Color: [ build.Color ]});
} else {
results[index] = {Product: build.Product, Color: [ ...results[index].Color, build.Color ]}
}
});
console.log(results);
The main issue in your code, was that you're mixing up arrays and key-value-pairs for color. KVP's look like { color: 'red' }, whereas arrays would be: [ 'red', 'blue'].
Use this strategy:
Find unique products
Based on the unique products use Array#map and Array#filter to build desired data
See demo below:
const builds = [ { 'Product': 'Cabinets', 'Color': 'Blue' }, { 'Product': 'CounterTop', 'Color': 'White' }, { 'Product': 'Cabinets', 'Color': 'Yellow' }, { 'Product': 'Cabinets', 'Color': 'Yellow' } ],
output = [...new Set(builds.map(({Product}) => Product))]
.map(Product =>
({
Product,
Color:builds.filter(({Product:P}) => P === Product)
.map(({Color}) => Color)
})
);
console.log(output);

How do I pick random data from an array and make sure that a minimum of a certain data are selected from that array without effecting probability?

I am selecting randomly 20 bikes from a larger array of bikes, and then creating a new array with those selected bikes. I would like to make sure that at least 2 of the randomly selected bikes in that new array are the color red. More than 2 can be red, but at a minimum 2 should be. How is this achieved while making it fair to all of the randomly selected bikes?
Some example code:
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
...more bikes
]
const getRandomBikes = () => {
const randomBikes = []
//random select 20 bikes
for(let i = 0; i < 20; i++) {
randomBikes.push(bikes[Math.floor(Math.random() * bikes.length)])
}
return randomBikes
}
getRandomBikes()
So pretty simple to return a random array. But now how would I make sure that the random bike array contained at least two bikes with color: 'red' without effecting the probability of every bike being selected? Presumably I would be running getRandomBikes() until the returned array satisfied the requirements, but say my array of bikes is 10000 long, that may take forever before the requirement is met. How do I ensure at least two red bikes end up in the randomly selected bike array?
While iterating, you might count up the number of required bikes of a specific color remaining, and if equal to the number of items that remain to be chosen, pick only from the bikes with the right color.
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
];
const getRandomBikes = (requiredRed, totalPicks) => {
const chosenBikes = [];
const reds = bikes.filter(({ color }) => color === 'red');
for(let i = 0; i < totalPicks; i++) {
const redsSoFar = chosenBikes.reduce((a, b) => a + (b.color === 'red'), 0);
const picksRemaining = totalPicks - i;
const mustChooseRed = requiredRed - redsSoFar === picksRemaining;
chosenBikes.push(
mustChooseRed
? reds[Math.floor(Math.random() * reds.length)]
: bikes[Math.floor(Math.random() * bikes.length)]
);
}
return chosenBikes;
}
console.log(getRandomBikes(2, 3));
Or with less complexity
const bikes = [
{ type: 'bmx', color: 'red' },
{ type: 'mountain', color: 'blue' },
{ type: 'bmx', color: 'yellow' },
{ type: 'mountain', color: 'black' },
{ type: 'bmx', color: 'red' },
{ type: 'bmx', color: 'purple' }
];
const getRandomBikes = (requiredRed, totalPicks) => {
const chosenBikes = [];
const reds = bikes.filter(({ color }) => color === 'red');
let redsSoFar = 0;
for(let i = 0; i < totalPicks; i++) {
const picksRemaining = totalPicks - i;
const mustChooseRed = requiredRed - redsSoFar === picksRemaining;
const chosenBike = mustChooseRed
? reds[Math.floor(Math.random() * reds.length)]
: bikes[Math.floor(Math.random() * bikes.length)]
if (chosenBike.color === 'red') redsSoFar++;
chosenBikes.push(chosenBike);
}
return chosenBikes;
}
console.log(getRandomBikes(2, 3));

Javascript force insert string into JS object (JSON) leaflet vectortiles

I am using leaflet mapping library
for styling purposes this code works for the data_oh.parcel layer if I hardcode the layer name like this
var vectorTileOptions = {
interactive: true,
vectorTileLayerStyles: {
'data_oh.parcel': {
fillColor: "yellow",
fill: true,
color: "red"
}
}
};
However as I am going to be adding multiple layers I need to add that layer name as a variable so I have something like this
var layers={parcels: ["#Parcels","http://localhost:7800/data_oh.parcel/{z}/{x}/{y}.pbf",'#ffe4c4','data_oh.parcel'],
footpring: ["#Footprints","http://localhost:7800/data_oh.footprint/{z}/{x}/{y}.pbf",'#8b8878','data_oh.footprint']
};
var idArray = [];
var layerArray = [];
function legend_click(id, layer_api, color_layer,layer_name) {
$(id).click(function () {
var layer_add;
var i = idArray.indexOf(id);
if (i < 0) {
layer_add = L.vectorGrid.protobuf(layer_api, {
interactive: true,
rendererFactory: L.svg.tile,
vectorTileLayerStyles: {
layer_name: {
fillColor: "yellow",fill: true, color: "red"
}
}
})
layerArray.push(layer_add);
idArray.push(id);
}
else {
layer_add = layerArray[i];
};
if ($(id).prop('checked') == true) {
layer_add.addTo(map);
}
else if ($(id).prop('checked') == false) {
map.removeLayer(layer_add);
}
})
};
for (var key in layers){
legend_click(layers[key][0],layers[key][1],layers[key][2],layers[key][3])
};
In the legend_click function the 4th input is for that layer name but the color is not changing on the map, this means the 4th input is not being recognized correctly in the vectorTileLayerStyles portion of the code. Again if I hardcode the values it works but passing through on a variable holding a string it does not.
You can use computed property names inside legend_click:
vectorTileLayerStyles: {
[`${layer_name}`]: {
fillColor: "yellow",fill: true, color: "red"
}
}
so if that did not work this should work:
vectorTileLayerStyles: Object.fromEntries([[layer_name,{fillColor: "yellow", fill: true, color: "red"}]])

JavaScript ES6: How to Iterate Over Object with [Symbol()] names

I'm trying to figure out how to iterate over an object that is using Symbol names to uniquely identify properties. For instance, if I have this object:
const bowl1 = {
'apple': { color: 'red', weight: 136.078 },
'banana': { color: 'yellow', weight: 183.151 },
'orange': { color: 'orange', weight: 170.097 },
'peach': { color: 'yellow', weight: 176.845 }
};
for (var fruit in bowl1) {
var item = bowl1[fruit];
console.log(`${fruit}: `, item);
}
OUTPUT:
apple: { color: 'red', weight: 136.078 }
banana: { color: 'yellow', weight: 183.151 }
orange: { color: 'orange', weight: 170.097 }
peach: { color: 'yellow', weight: 176.845 }
// can even write your own iterator to get the same results
function* iterate_object(o) {
var keys = Object.keys(o);
for (var i = 0; i < keys.length; i++) {
yield [keys[i], o[keys[i]]];
}
}
// output is the same as above
for (var [key, val] of iterate_object(bowl1)) {
console.log(key, val);
}
However, if I change this object to use Symbols as such:
const bowl = {
[Symbol('apple')]: { color: 'red', weight: 136.078 },
[Symbol('banana')]: { color: 'yellow', weight: 183.15 },
[Symbol('orange')]: { color: 'orange', weight: 170.097 },
[Symbol('banana')]: { color: 'yellow', weight: 176.845 }
};
Note that symbols are used to keep the second banana from overwriting the first.
Anyway, neither method used above will iterate properly over this object.
Is there a way to iterate over objects using Symbol names?
Does it need to be created as a class and have an iterator method?
Thanks in advance for the help.
You can't get symbol property names because they're not stored as typical character/string values, but you can iterate over the list returned by Object.getOwnPropertySymbols and use those to pull information out of an Object.
const bowl = {
[Symbol('apple')]: { color: 'red', weight: 136.078 },
[Symbol('banana')]: { color: 'yellow', weight: 183.15 },
[Symbol('orange')]: { color: 'orange', weight: 170.097 },
[Symbol('banana')]: { color: 'yellow', weight: 176.845 }
};
for(let sym of Object.getOwnPropertySymbols(bowl) ) {
console.log(bowl[sym]);
}
As stated above you cannot get the symbol property names because they are not stored as typical character strings. However, the MDN site does say β€œIn addition, Object.getOwnPropertyNames() will not return symbol object properties, however, you can use Object.getOwnPropertySymbols() to get these.”
As such, on order to get information about the objects use Object.getOwnPropertySymbols:
for(let sym of Object.getOwnPropertySymbols(bowl) ) {
console.log(sym, bowl[sym]);
}
OUTPUT:
Symbol(apple) { color: 'red', weight: 136.18 }
Symbol(banana) { color: 'yellow', weight: 183.15 }
Symbol(orange) { color: 'orange', weight: 170.97 }
Symbol(banana) { color: 'yellow', weight: 176.84 }
As you can see you don’t get the symbol names as strings, but it will return the symbol and their referenced objects.
You have to do some extra work if you want to convert the symbol to a string and just see the property name.
for(let sym of Object.getOwnPropertySymbols(bowl) ) {
// remove the 'Symbol (' from string
let symStr = String(sym).substring(7);
// now lop off the last char ')'
symStr = symStr.slice(0, -1);
console.log(symStr, bowl[sym]);
}
OUTPUT:
apple { color: 'red', weight: 136.18 }
banana { color: 'yellow', weight: 183.15 }
orange { color: 'orange', weight: 170.97 }
banana { color: 'yellow', weight: 176.84 }
You can combine the string functions on one line if you want.
let symStr = String(sym).substring(7).slice(0, -1);
Maybe not the best use for Symbols and there are both global and local symbols. The example used above and this is from lesson material are local.
Acutally there are two methods, will share it for your reference,
const bowl = {
[Symbol('apple')]: { color: 'red', weight: 136.078 },
[Symbol('banana')]: { color: 'yellow', weight: 183.151 },
[Symbol('orange')]: { color: 'orange', weight: 170.097 },
[Symbol('banana')]: { color: 'yellow', weight: 176.845 }
};
const bowlReflect = Reflect.ownKeys(bowl);
const ownPropertySymbols = Object.getOwnPropertySymbols(bowl);
**Method:1**
for(const fruit of bowlReflect){
console.log(bowl[fruit])
}
Method:2
for(const fruit of ownPropertySymbols){
console.log(bowl[fruit])
}
const fantasma = Symbol('fantasma');
console.log(fantasma);
// Symbol(fantasma)
const objetoLiteral = {
// propiedad: valor,
[fantasma]: 'πŸ‘»', // Symbol()
uno: 1,
dos: 2,
tres: 3,
};
console.log(objetoLiteral);
/*
{
uno: 1,
dos: 2,
tres: 3,
[Symbol(fantasma)]: 'πŸ‘»'
}
*/
Object.getOwnPropertySymbols(objetoLiteral).map((propiedad) => {
const valor = objetoLiteral[propiedad];
console.log(propiedad, '➜', valor);
});
// Symbol(fantasma) ➜ πŸ‘»

Merge two objects with override in javascript [duplicate]

This question already has answers here:
How to deep merge instead of shallow merge?
(47 answers)
Closed 5 years ago.
I want to merge two objects, override properties but keep properties that are not been overridden.
Example: I have the following Objects
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
and
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
and would like to merge these together to get
const newTheme = {
colors: {
base: '#fff',
accent: '#ff8900'
}
}
If you just want to merge the property color of theme and themeOverride, you can do it by the code below:
var theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
};
var themeOverride = {
colors: {
accent: '#ff8900'
}
};
Object.assign(theme.colors, themeOverride.colors);
console.log(theme);
You can use Object.assign to merge these objects
Update existing object
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
Object.assign(theme.colors, themeOverride.colors)
console.log(theme)
Or create new object
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
newTheme = { colors: Object.assign({}, theme.colors, themeOverride.colors) }
console.log(newTheme)
You could merge by iterateing all properties for update with a recursive approach for objects.
function merge(target, source) {
Object.keys(source).forEach(function (key) {
if (source[key] && typeof source[key] === 'object') {
merge(target[key] = target[key] || {}, source[key]);
return;
}
target[key] = source[key];
});
}
var theme = { colors: { base: '#fff', accent: '#ff0000' } },
themeOverride = { colors: { accent: '#ff8900' } };
merge(theme, themeOverride);
console.log(theme);
JS doesn't have a built-in way to do this, but it's very simple to do with Lodash, or Underscore's _.merge() or Ramda's _.mergeDeepLeft(), all of which recursively merge objects.
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
const newTheme = _.merge(theme, themeOverride);
console.log(newTheme);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>
You can use reduce with old theme as initial value. Try something like this:
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
},
}
const themeOverride = {
colors: {
accent: '#ff8900'
},
border: {
borderWidth: '2px'
}
}
const newTheme = Object.keys(themeOverride).reduce((prev, key) => {
prev[key] = Object.assign({}, theme[key] || {}, themeOverride[key])
return prev
}, Object.assign({}, theme))
console.log(newTheme)
Note, this solution expects maximum 2 level nesting.
Iterate both objects, look for intersection and override in that instance; else, just copy as such;
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
window.onload = mergeObjects(theme,themeOverride)
function mergeObjects(base,override) {
var mergedObj = {'colors' : {}};
for(key in base["colors"]) {
if(override['colors'][key] == undefined) {
mergedObj['colors'][key] = base['colors'][key]
}
else {
mergedObj['colors'][key] = override['colors'][key]
}
}
console.log('mergedObject is',mergedObj)
}
You could recursively look through your object and assign your new updated values this way.
Here, I made a function for it :
const theme = {
colors: {
base: '#fff',
accent: '#ff0000'
}
}
const themeOverride = {
colors: {
accent: '#ff8900'
}
}
function overrideObject(o1,o2){
var res = {};
//Go through all your attributes
for (var a in o1){
//Begin recursive method if another object is detected
if(typeof o1[a] == 'object'){
res[a] = overrideObject(o1[a],o2[a])
}
//Clone old data & update it if necessary
else{
res[a] = o1[a];
if(typeof o2[a] != 'undefined') res[a] = o2[a];
}
}
return res;
}
console.log(overrideObject(theme,themeOverride));

Categories

Resources