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

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) ➜ πŸ‘»

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

I need help for merging values in object array - Javascript

I am working with one project , I have the data comes to me as Object Array and I need to combine the same keys in one key and make the value as an array of strings.
here is the data I have :
inputArray = [
{
colors: 'Red',
size: 'Small'
},
{
colors: 'Blue',
size: 'Large'
},
{
colors: 'Red',
size: 'Large'
},
{
colors: 'Pink',
size: 'X-Large'
}
]
and here is the required output :
outputArray = {
colors: ['Red','Blue','Pink'],
size: ['Large','X-large','Small']
}
You could use a simple dictionary structure to do this. And verify if every element already exists before adding it to array.
const outputArray = {
colors: [],
size: [],
};
for (elem of inputArray) {
if (!outputArray['colors'].includes(elem.colors)) {
outputArray['colors'].push(elem.colors);
}
if (!outputArray['size'].includes(elem.size)) {
outputArray['size'].push(elem.size);
}
}
which will give
{
colors: [ 'Red', 'Blue', 'Pink' ],
size: [ 'Small', 'Large', 'X-Large' ]
}
it's a basic one...
const inputArray =
[ { colors: 'Red', size: 'Small' }
, { colors: 'Blue', size: 'Large' }
, { colors: 'Red', size: 'Large' }
, { colors: 'Pink', size: 'X-Large'}
];
outputArray = inputArray.reduce((a,c)=>
{
if (!a.colors.includes(c.colors) ) a.colors.push( c.colors);
if (!a.size.includes(c.size) ) a.size.push( c.size);
return a
}
,{ colors:[], size:[]})
;
console.log (outputArray )
[edit] if you do not know the variety of entry keys, you can use:
inputArray =
[ { colors: 'Red', size: 'Small' }
, { colors: 'Blue', size: 'Large' }
, { colors: 'Red', size: 'Large' }
, { colors: 'Pink', size: 'X-Large', truc: 'bidule' }
];
outputArray = inputArray.reduce((a,c)=>
{
for (let key in c)
{
if (!a[key]) a[key] = []
if (!a[key].includes(c.colors) ) a[key].push( c[key])
}
return a
} ,{})
;
console.log (outputArray)
This seems to work...
let inputArray = [
{
colors: 'Red',
size: 'Small'
},
{
colors: 'Blue',
size: 'Large'
},
{
colors: 'Red',
size: 'Large'
},
{
colors: 'Pink',
size: 'X-Large'
}
]
let outputArray = [{colors: [], size: []}]
for (let i = 0; i<inputArray.length; i++){
outputArray[0].colors.push(inputArray[i].colors)
outputArray[0].size.push(inputArray[i].size)
}
console.log(outputArray)
Is this what you were after?
While this is not logically much different from the second part of the answer by Mister Jojo, it does the same thing without any mutations, in perhaps a more functional manner:
const gather = (xs) =>
xs .reduce (
(a, x) => Object .entries (x) .reduce ((a, [k, v]) => ({...a, [k]: (a[k] || []).concat(v)}), a),
{}
)
const inputArray = [{ colors: 'Red', size: 'Small'}, {colors: 'Blue', size: 'Large'}, {colors: 'Red', size: 'Large'}, {colors: 'Pink', size: 'X-Large'}]
console .log (gather (inputArray))
It is likely less performant than that version, for reasons described by Rich Snapp, but in practice I haven't seen this being a real issue.

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

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

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