Related
I have an array of arrays which I want to marge into one and remove duplicate values.
let arr = [ {
label :'XYZ',
colors:['black','white','blue']
},
{
label :'PQR',
colors:['orange','yellow','white']
},
{
label :'ABC',
colors:['black','pink','blue']
},
]
let updatedArr = []
for(let i=0 i< arr.length ; i++ ){
updatedArr.push(
arr[i].reduce((a, b) => [...a, ...b], [])
)
}
I want all the colors array value into one array and remove duplicate values as well.
Any help would be great.
To get a concatenated list of all values you can use flatMap().
And to deduplicate you can use [...new Set(duplicates)], which creates a Set of unique elements and spreads it into an array.
let arr = [{
label: 'XYZ',
colors: ['black', 'white', 'blue']
},
{
label: 'PQR',
colors: ['orange', 'yellow', 'white']
},
{
label: 'ABC',
colors: ['black', 'pink', 'blue']
},
]
let colors = [...new Set(arr.flatMap(obj => obj.colors))]
console.log(colors)
You can use reduce and forEach. Inside reduce call back use forEach to iterate the colors array & if accumulator does not include the color then push that color to accumulator
let arr = [{
label: 'XYZ',
colors: ['black', 'white', 'blue']
},
{
label: 'PQR',
colors: ['orange', 'yellow', 'white']
},
{
label: 'ABC',
colors: ['black', 'pink', 'blue']
},
]
let mergedColor = arr.reduce((acc, curr) => {
curr.colors.forEach((item) => {
if (acc.indexOf(item) === -1) {
acc.push(item)
}
})
return acc;
}, []);
console.log(mergedColor)
Use Array.prototype.flat for flatting the array, and then use a Set for distincting values and then create an array from the set using the Spread operator.
let arr = [{
label: 'XYZ',
colors: ['black', 'white', 'blue']
},
{
label: 'PQR',
colors: ['orange', 'yellow', 'white']
},
{
label: 'ABC',
colors: ['black', 'pink', 'blue']
},
]
const vals = [...new Set(arr.map(i => i.colors).flat())]
console.log(vals)
I have a jsfiddle with 3 parents ( Banana, apple, orange ) and when treemap renders for example Banana has objects with values ( Rick, Anne, Susane ) and what it renders is same color ( yellow in this example ) and my goal is for example if value of that object is 10 to be yellow with no blurred to yellow, and if value of that object is 1 to be almost white with hint of yellow.
Is there an algorithm that handles that ? I am quite sure that this is already known request from people.
For now I managed to create in one fiddle those parents with childs, and in other I managed to produce different versions of shades of that specific color
this is data of parents and kids
{
id: 'A',
name: 'Apples',
color: "#EC2500"
}, {
id: 'B',
name: 'Bananas',
color: "#ECE100"
}, {
id: 'O',
name: 'Oranges',
color: '#EC9800'
}, {
name: 'Anne',
parent: 'A',
value: 5
}, {
name: 'Rick',
parent: 'A',
value: 3
}, {
name: 'Peter',
parent: 'A',
value: 4
}, {
name: 'Anne',
parent: 'B',
value: 4
}
and this is result that I would like to implement into fiddle above with different shades of yellow, red and orange
{
colorAxis: {
minColor: '#FFFFFF',
maxColor: '#FFFF33'
// maxColor:Highcharts.getOptions().colors[0]
},
series: [{
type: 'treemap',
layoutAlgorithm: 'squarified',
data: [{
name: 'Rick',
value: 6,
colorValue: 1
}, {
name: 'Anne',
value: 6,
colorValue: 2
}, {
name: 'Susane',
value: 4,
colorValue: 3
}, {
name: 'Peter',
value: 3,
colorValue: 4
}, {
name: 'E',
value: 2,
colorValue: 5
}, {
name: 'F',
value: 2,
colorValue: 6
}, {
name: 'G',
value: 1,
colorValue: 7
}]
}],
title: {
text: 'Highcharts Treemap'
}
}
This is the fiddle with shades of yellow
https://jsfiddle.net/bpc7fd49/1/
and this is fiddle where I am trying to implement those shades instead of having only one version of yellow red or orange color
https://jsfiddle.net/7z5ngLva/3/
If you do not want to use the colorAxis form heatmap, you can calculate the colors for points by using the tweenTo internal method:
(function(H) {
var treemapProto = Highcharts.seriesTypes.treemap.prototype,
minVal,
maxVal,
values,
children;
Highcharts.wrap(treemapProto, 'translate', function(proceed) {
proceed.apply(this, Array.prototype.slice.call(arguments, 1));
this.values = {};
this.points.forEach(function(point) {
children = point.node.children;
if (children.length) {
minVal = null;
maxVal = null;
children.forEach(function(child) {
minVal = minVal ? Math.min(minVal, child.val) : child.val;
maxVal = maxVal ? Math.max(maxVal, child.val) : child.val;
});
this.values[point.id] = {
minVal: minVal,
maxVal: maxVal,
minColor: point.minColor,
maxColor: point.maxColor
};
} else {
values = this.values[point.parent];
point.color = H.color(values.minColor).tweenTo(
H.color(values.maxColor),
(point.value - values.minVal) / (values.maxVal - values.minVal)
);
}
}, this);
});
})(Highcharts);
Live demo: https://jsfiddle.net/BlackLabel/b1k5whcv/
Docs: https://www.highcharts.com/docs/extending-highcharts
I've managed to find an answer
stops = [
[0.25, '#EC2500'],
[0.5, '#ECE100'],
[0.75, '#EC9800'],
[1, '#9EDE00']
],
distance = stops[1][0] - stops[0][0],
modifiedStops = [];
$.each(stops, function (i, stop) {
modifiedStops.push([stop[0] - distance - 0.001, '#ffffff']);
modifiedStops.push(stop);
modifiedStops.push([stop[0] + 0.001, stop[1]]);
});
whole fiddle http://jsfiddle.net/4egy85fw/
Suppose if I have two arrays - one as the preference in order and another the data set and I want to return first element from data set matching first matching preference.
For example
const userPref = ['banana', 'apple', 'peach'];
const givenFruits = [
{ name: 'apple', color: 'red' },
{ name: 'orange', color: 'orange' },
{ name: 'pear', color: 'yellow' },
{ name: 'cherry', color: 'red' },
{ name: 'grape', color: 'red' },
{ name: 'peach', color: 'red' },
{ name: 'coconut', color: 'brown' }
];
function findFavFruit() {
userPref.forEach((pref) => {
givenFruits.forEach((fruit) => {
if(pref === fruit.name) {
return fruit;
}
});
});
}
console.log('findFavFruit(): ' + JSON.stringify(findFavFruit(), null, 2));
This is always returning undefined. It supposed to return apple ONLY as it is user first matching preference and is found first in givenFruits.
What I am doing wrong in above code? And is there a cleaner way (avoid double forEach) in Javascript?
You can loop over the givenFruits using for...of and use Array.includes to test if the current fruit is inside the array of favorite fruits..
Example:
function findFavoriteFruit(preferences, arrayOfFruits) {
for (let fruit of arrayOfFruits) {
if (preferences.includes(fruit.name)) {
return fruit;
}
}
}
const userPref = ['apple', 'banana', 'peach'];
const givenFruits = [
{ name: 'apple', color: 'red' },
{ name: 'orange', color: 'orange' },
{ name: 'banana', color: 'yellow' },
{ name: 'pear', color: 'yellow' },
{ name: 'cherry', color: 'red' },
{ name: 'grape', color: 'red' },
{ name: 'peach', color: 'red' },
{ name: 'coconut', color: 'brown' }
];
const favoriteFruit = findFavoriteFruit(userPref, givenFruits);
console.log(favoriteFruit);
This implementation is the fastest (comparing with other answers) as you can see here.
Select the first element of userPref array to compare fruit.name to within .find() function, return result.
To return only the property value, for example, "name", you can pass the property as a string to the function and use bracket notation to reference and return the property
const userPref = ['apple', 'banana', 'peach'];
const [preference] = userPref;
const givenFruits = [
{ name: 'apple', color: 'red' },
{ name: 'orange', color: 'orange' },
{ name: 'banana', color: 'yellow' },
{ name: 'pear', color: 'yellow' },
{ name: 'cherry', color: 'red' },
{ name: 'grape', color: 'red' },
{ name: 'peach', color: 'red' },
{ name: 'coconut', color: 'brown' }
];
function findFavFruit(pref, prop, arr) {
return arr.find(fruit => pref === fruit[prop])[prop];
}
let res = findFavFruit(preference, "name", givenFruits);
console.log(res);
Of course you should provide some checks whether given keys exist, but here is the basic:
(givenFruits.find((v)=>{return v.name == userPref[0]})).name
Don't know why to iterate over userPref if you only need first key.
All of the above solutions are correct, I just wanted to clarify what the issue with your code was. See below:
function findFavFruit() {
let userFruit;
userPref.forEach((pref) => {
givenFruits.forEach((fruit) => {
if(pref === fruit.name) {
userFruit = fruit;
}
});
});
return userFruit;
}
This would return the fruit if it was found or undefined if it wasn't.
Given the following two arrays of objects :
var items = [
{colorId:'2',name:'qqq'},
{colorId:'5',name:'www'},
{colorId:'2',name:'eee'},
{colorId:'4',name:'rrr'}
];
var colors = [
{id:'5',name:'blue'},
{id:'2',name:'red'}
];
I need to make a join between the items and colors based on the colorId.
The desired result :
var arr3 = [
{id:'2', name:'qqq', name:'red'},
{id:'5', name:'www', name:'blue'},
{id:'2', name:'eee', name:'red'}
];
What is a elegant way to do this ?
this a pure JavaScript solution, which will change the items array itself.
if you don't want to modify the same array, you can create new array and push cloned items into it.
var items= [{colorId:'2',name:'qqq'},
{colorId:'5',name:'www'},
{colorId:'2',name:'eee'},
{colorId:'4',name:'rrr'}]
var colors= [{id:'5',name:'blue'},
{id:'2',name:'red'}, ]
items.forEach(function(d) {
var matchColor = colors.filter(function(item){ return item.id === d.colorId});
if(matchColor.length){
d.color = matchColor[0].name;
}
});
console.log(items);
Using _.map you can add the color value to each element and return a new array.
It looks like this:
var newItems = _.map(items, function(item) {
// `black` will be the default fallback color
// We use _.result in case we can't find the colorId in the colors array
var color = _.result(_.find(colors, { id: item.colorId }), 'name', 'black');
// Append the color to the original item in the array
item.color = color
// Return the modified item
return item;
});
// newItems:
// [
// { "colorId": "2", "name": "qqq", "color": "red" },
// { "colorId": "5", "name": "www", "color": "blue" },
// { "colorId": "2", "name": "eee", "color": "red" },
// { "colorId": "4", "name": "rrr", "color": "black" }
// ]
A nice way is to put the names together in an array where the id is the same. The id is the key and is a number.
Structure :
var data = {
id: { // id is a number
names: [],
color: ""
}
};
Example :
var items= [{colorId:'2',name:'qqq'},
{colorId:'5',name:'www'},
{colorId:'2',name:'eee'},
{colorId:'4',name:'rrr'}];
var colors= [{id:'5',name:'blue'},
{id:'2',name:'red'},
{id: '1', name: 'violett'}];
// Result
var data = {};
items.map(function(obj) {
if(!(obj.colorId in data)) {
data[obj.colorId] = {};
data[obj.colorId].names = [];
}
data[obj.colorId].names.push(obj.name);
});
colors.map(function(obj) {
if(!(obj.id in data)) {
data[obj.id] = {};
data[obj.id].names = [];
}
data[obj.id].color = obj.name;
});
console.log(data);
You could use a hash table and loop every array only once.
var items = [{ colorId: '2', name: 'qqq' }, { colorId: '5', name: 'www' }, { colorId: '2', name: 'eee' }, { colorId: '4', name: 'rrr' }],
colors = [{ id: '5', name: 'blue' }, { id: '2', name: 'red' }],
arr3 = [];
items.forEach(function (hash) {
colors.forEach(function (a) {
hash[a.id] = a.name;
});
return function (a) {
hash[a.colorId] && arr3.push({ id: a.colorId, name: a.name, color: hash[a.colorId] });
};
}(Object.create(null)), []);
console.log(arr3);
ES6
var items = [{ colorId: '2', name: 'qqq' }, { colorId: '5', name: 'www' }, { colorId: '2', name: 'eee' }, { colorId: '4', name: 'rrr' }],
colors = [{ id: '5', name: 'blue' }, { id: '2', name: 'red' }],
arr3 = [];
items.forEach((hash => {
colors.forEach(a => hash[a.id] = a.name);
return a => hash[a.colorId] && arr3.push({ id: a.colorId, name: a.name, color: hash[a.colorId] });
})(Object.create(null)), []);
console.log(arr3);
I'm having trouble understanding the following reduce function:
function findDeep(arr, obj) {
return arr.map(item => {
if (item.name === obj.name) {
return arr
} else if (item.children) {
return findDeep(item.children, obj)
} else {
return undefined
}
}).reduce((prev, curr) => {
console.log('prev: ', prev)
console.log('curr: ', curr)
return prev || curr
})
}
Applied to this object:
const mockApps = {
name: 'orange',
children: [{
name: 'white'
}, {
name: 'green',
children: [{
name: 'yellow',
children: [{
name: 'red'
}, {
name: 'white'
}]
}, {
name: 'green',
children: [{
name: 'purple'
}]
}]
}, {
name: 'gray'
}]
}
const activeApp = {
name: 'purple',
color: 'purple',
path: 'writer'
}
findDeep(mockApps.children, activeApp)
I thought the pattern would be like the example at MDN:
[0, 1, 2, 3, 4].reduce(function(previousValue, currentValue, currentIndex, array) {
return previousValue + currentValue;
});
But to my surprise what I theorized was different from the output:
I thought the previousValue would be the returnValue of the previous iteration, but as you can see in the console, the third prev is undefined even if the currentValue of the previous iteration is not.
What's the correct pattern of this reduce function?
Here's the CodePen.
If you follow the code through, the first value passed to map (level 0) is:
{name: 'white'}
It doesn't have a name of purple, or any children so the result array is now:
[undefined]
The next item is:
{name: 'green',
children: [{
name: 'yellow',
children: [{
name: 'red'
}, {
name: 'white'
}]
}, {
name: 'green',
children: [{
name: 'purple'
}]
}]
}
It has a children property so its value is passed to a recursive call to findDeep (level 1), which is:
[{
name: 'yellow',
children: [{
name: 'red'
}, {
name: 'white'
}]
}, {
name: 'green',
children: [{
name: 'purple'
}]
}]
The first item passed to map, and again findDeep is called recursively (level 2) with:
[{name: 'red'},
{name: 'white'}]
}, {
name: 'green',
children: [{
name: 'purple'
}]
the first item has no name of purple or children, so this level map array is now:
[undefined]
Same for the next item, so now it's:
[undefined, undefined]
The next has a name 'purple', so it's added to the array:
[undefined, undefined,{name:'purple'}]
That is run through reduce, which is called with no accumulator so the first two values are passed as prev and cur. Since prev is falsey, the value of curr is returned as the accumulator so on the next call the values are undefined and {name:'purple'}, so that's returned to the level 1 map and it's array is now:
[{name:'purple'}]
There are no more members in this level, so that is passed to reduced. Since it's the only member in the array and there's no accumulator passed in, it's simply returned, so the level 1 result is:
[{name:'purple'}]
The last member at level 0 also returns undefined, so the final level 0 array is:
[{name:'purple'}, undefined]
Which is passed to reduce, with the two values being prev and curr respectively. Since prev the object isn't falsey, it's returned and the final result is:
[{name:'purple'}]
Note that if you use JSON.stringify to look at objects and arrays, undefined is changed to "null".
It seems you are assuming the console output belongs to one run of reduce, but this is not true.
The function findDeep calls itself recursively, so you'll get output from distinct calls to reduce.
I suggest you modify the code as follows to also see in the console when findDeep is being called and exited:
function findDeep(arr, obj) {
console.log('Entering findDeep');
var res = arr.map(item => {
if (item.name === obj.name) {
return arr
} else if (item.children) {
return findDeep(item.children, obj)
} else {
return undefined
}
}).reduce((prev, curr) => {
console.log('prev: ' + JSON.stringify(prev));
console.log('curr: ' + JSON.stringify(curr));
console.log('return: ' + JSON.stringify(prev || curr));
return prev || curr;
});
console.log('Exiting from findDeep');
return res;
}
This should bring light to the issue. Here is a snippet that writes the log to the browser:
console = { log: function(msg) {
document.write(msg + '<br>');
}}
function findDeep(arr, obj) {
console.log('Entering findDeep');
var res = arr.map(item => {
if (item.name === obj.name) {
return arr
} else if (item.children) {
return findDeep(item.children, obj)
} else {
return undefined
}
}).reduce((prev, curr) => {
console.log('prev: ' + JSON.stringify(prev));
console.log('curr: ' + JSON.stringify(curr));
console.log('return: ' + JSON.stringify(prev || curr));
return prev || curr;
});
console.log('Exiting from findDeep');
return res;
}
const mockApps = {
name: 'orange',
children: [{
name: 'white'
}, {
name: 'green',
children: [{
name: 'yellow',
children: [{
name: 'red'
}, {
name: 'white'
}]
}, {
name: 'green',
children: [{
name: 'purple'
}]
}]
}, {
name: 'gray'
}]
}
const activeApp = {
name: 'purple',
color: 'purple',
path: 'writer'
}
findDeep(mockApps.children, activeApp)
As you can see, where previously it seemed the value of prev did not correspond to the return value of the previous iteration, it now becomes clear that these two iterations belong to a different call of reduce, so it acts just like you expect.