merging specific object values from array into separated array - javascript

I have this array of persons
personsList = [
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C', 'D']}
]
in every object inside there more details beside cities but I want to make an array with all the cities inside - unique. so the result will be
['A', 'B', 'C', 'D']
I tried doing something like
const allCities = personsList.map(p => p.cities)
but then I got an array of arrays, what is the best approach for this one ? thanks.

You can try following using Set
var personsList = [
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C', 'D']}
]
// cities will have all the cities with duplicates
const allCities = personsList.reduce((a,c) => [...a, ...c.cities], []);
// create set from array - removes duplicate and then create array back from set
console.log(Array.from(new Set(allCities)));

You can array#map all cities and then concat them into one array. Then using Set you can get the unique city.
const personsList = [ {cities: ['A', 'B', 'C']}, {cities: ['A', 'B', 'C']}, {cities: ['A', 'B', 'C']}, {cities: ['A', 'B', 'C', 'D']} ],
uniqueCity = Array.from(new Set([].concat(...personsList.map(({cities}) => cities))));
console.log(uniqueCity);

You could use directly a Set for getting unique values.
var personsList = [{ cities: ['A', 'B', 'C'] }, { cities: ['A', 'B', 'C'] }, { cities: ['A', 'B', 'C'] }, { cities: ['A', 'B', 'C', 'D'] }],
unique = Array.from(
personsList.reduce(
(s, { cities }) => cities.reduce((t, c) => t.add(c), s),
new Set
)
);
console.log(unique);

I think this is the effect that you are looking for, by using set you guarantee yourself that there are only unique elements.
let personsList = [
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C']},
{cities: ['A', 'B', 'C', 'D']}
]
const allCities = personsList.reduce((acc,e) => {
e.cities.map(x=>{
acc.add(x)
})
return acc
}, new Set())
console.log(Array.from(allCities))

const personsList = [
{ cities: ['A', 'B', 'C'] },
{ cities: ['A', 'B', 'C'] },
{ cities: ['A', 'B', 'C'] },
{ cities: ['A', 'B', 'C', 'D'] }
];
const allCities = [...new Set([].concat(...personsList.map(({ cities }) => cities)))];
console.log(allCities);

Related

How to convert an array into an object of specific format?

Although it is a common problem but I couldn't find any lead to get the desired result. So here is the problem. I have the following array:
[
[ 'a' ]
[ 'a', 'b' ]
[ 'a', 'c' ]
[ 'a', 'c', 'd' ]
[ 'a', 'c', 'd', 'e' ]
]
And what I want as an end result is an object like this:
{
a: {
b: {},
c: { d: { e: {} } }
}
}
I don't understand which approach would be better to get this result and how to achieve it.
You need a double reduce, one for the outer array and one for the keys and the nesting objects.
var data = [['a'], ['a', 'b'], ['a', 'c'], ['a', 'c', 'd'], ['a', 'c', 'd', 'e']],
result = data.reduce((r, keys) => {
keys.reduce((o, k) => o[k] = o[k] || {}, r);
return r;
}, {});
console.log(result);

Sort an Object so that Largest Nested Arrays Come First [duplicate]

This question already has answers here:
Sort JavaScript object by key
(37 answers)
Closed 2 years ago.
Rookie Javascript question here: I have an object containing arrays like so
const obj = {
'red': [ 'a', 'b', 'c'],
'blue': [ 'a' ],
'green': [ 'a', 'b', 'c', 'd', 'e' ]
}
I want to sort my object so that the properties with the largest arrays come first
obj = {
'green': [ 'a', 'b', 'c', 'd', 'e' ],
'red': [ 'a', 'b', 'c'],
'blue': [ 'a' ]
}
Is there a simple one-liner or lodash method I can achieve this with?
Convert your object into an array. Sort the array according to your needs then re-build an object from that array as below :
const obj = {
'red': ['a', 'b', 'c'],
'blue': ['a'],
'green': ['a', 'b', 'c', 'd', 'e']
};
const ordered = {};
const asArray = Object.keys(obj).map(key => ({
key,
arr: obj[key]
})); // create an array from the object
asArray.sort((a, b) => b.arr.length - a.arr.length); // sor the array so the array has bigger length should come first
asArray.forEach(r => {
ordered[r.key] = r.arr
}); // re construct as new object
console.log(ordered);
First sort then assign
const obj = {
'red': [ 'a', 'b', 'c'],
'blue': [ 'a' ],
'green': [ 'a', 'b', 'c', 'd', 'e' ]
};
const newObj = {};
let sorted = Object.keys(obj)
.sort((a, b) => obj[b].length - obj[a].length).forEach(e => newObj[e] = obj[e]);
console.log(newObj);

filtering an array of objects based on a contained arrays contents. Vanilla JS, lodash, some other react releated technology?

So i'm making a react app and I have a list of projects that use specific technologies. I want the user to be able to be able to say i'm looking for a project that uses 'a', 'b', and 'c' technologies via some checkboxes (maybe) and the list of projects will automatically update. Should i just be using vanilla javascript for this (to sort through an array of objects) or is there an easier way to do it?
If this should be done via vanilla javascript, can someone give me a little direction please. was able to do this successfully search for just one 'technology' but couldn't quite get it done searching for multiple technologies.
Example data to be below.
const projects = [{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}];
You could use a destructuring while filtering with checking all wanted technologies.
const
projects = [{ name: 'projecta', technologies: ['a', 'b', 'f'], link: 'www.example.com' }, { name: 'projectb', technologies: ['c', 'd', 'e'], link: 'www.example.com' }, { name: 'projectc', technologies: ['a', 'b', 'c', 'd', 'e'], link: 'www.example.com' }],
search = ['c', 'e'],
result = projects.filter(({ technologies }) =>
search.every(s => technologies.includes(s)));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If you have multiple checkboxes you will get an array which contains the value of all selected checkboxes.
Filter the array based one or multiple values will work like this:
const projects = [
{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}
];
const search = ['c', 'e'];
const result = projects.filter(project => {
return search.every(s => project.technologies.includes(s));
});
// or
const result2 = projects.filter(project => {
return !search.some(s => project.technologies.indexOf(s) === -1);
});
console.log(result);
console.log(result2);
Yes you can do it using only vanilla JS, you can profit from Array's buil-in methods to do it.
You can use a combination of .filter() and .some() methods to filter your array based on the specified technologies list.
This is how should be your code:
const techs = ['a', 'c'];
var filtered = projects.filter(function(p){
return !techs.some(function(t){
return p.technologies.indexOf(t) === -1;
});
});
Demo:
const projects = [
{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}
];
const techs = ['a', 'c'];
var filtered = projects.filter(function(p){
return !techs.some(function(t){
return p.technologies.indexOf(t) === -1;
});
});
console.log(filtered);
Explanation:
We use filter() method to loop over the array filter its elements and in its callback function we use .some() method to test over the iterated project technologies to make sure they're all among of the given technologies list.
Assuming you store the selected technologies in an array, you could simply loop through each project with filter() and return those that have at least one of the techs.
In the snippet below we iterate through each project with filter, meaning we create an array with items that satisfy the criteria in the callback.
The callback does another filter for each tech of the current project, and returns the length of the filtered array. If no tech of the current project matches your selection, the array length is zero which is a falsy value and the whole object won't be returned to the new array.
For example:
let selectedTechs = ['a', 'b']; //selected techs
const projects = [
{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}
];
let arr = projects.filter(
(item) => item.technologies.filter((x) => selectedTechs.includes(x)).length
);
console.log(arr); //returns the 1st and 3rd project object.
and a demo in React:
class MyApp extends React.Component {
constructor() {
super();
this.state = {
technologies: ['a', 'b', 'c', 'd', 'e', 'f'],
checkedTechs: [],
projects: [
{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}
]
};
}
checkTech = (name) => {
let arr = this.state.checkedTechs.slice();
if(arr.includes(name)) {
arr.splice(arr.indexOf(name), 1);
} else {
arr.push(name);
}
this.setState({checkedTechs: arr});
}
render() {
let {technologies, checkedTechs, projects} = this.state;
return(
<div>
<div>
{technologies.map(
(item) => <label key={item}>{item}<input value={checkedTechs.indexOf(item)>-1} onChange={this.checkTech.bind(this, item)} type="checkbox" /></label>
)}
</div>
<ul>
{this.state.projects.filter(
(item) => item.technologies.filter((x) => checkedTechs.includes(x)).length
).map(
(item) => <li key={item.name}>{item.name}</li>
)}
</ul>
</div>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("app"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.js"></script>
<div id="app"></div>
The above is to show project that include the selected techs. If you want to select projects that have exactly the selected techs, replace the filter logic with:
(item) => item.technologies.join(',') === checkedTechs.sort().join(',')
You can use javascript for this. Say, in each click of checkbox your checkbox value is saved in choice then you can get the objects like this,
const projects = [
{
name: 'projecta',
technologies: ['a', 'b', 'f'],
link: 'www.example.com'
},
{
name: 'projectb',
technologies: ['c', 'd', 'e'],
link: 'www.example.com'
},
{
name: 'projectc',
technologies: ['a', 'b', 'c', 'd', 'e'],
link: 'www.example.com'
}
];
var choice = 'e';
var res= projects.filter((x)=>{
if(x.technologies.indexOf(choice) !== -1){
return x;
}
});
console.log(res);
I've found something that works. best solution?
const checkFor = ['a', 'b'];
const winners = projects.filter(project => {
return checkFor.every(function(val) {
return project.technologies.indexOf(val) !== -1;
});
});
console.log('winners: ', winners);

Can't find lodash.pull function for Ruby array

Is there any utility function which works like lodash.pull but for Ruby arrays?
You can use the delete method to remove occurrences of a single value:
arr = ['a', 'b', 'c', 'b', 'c', 'd']
arr.delete 'b'
# ['a', 'c', 'c', 'd']
Or you can use the - or -= operator to delete an array of values.
arr = ['a', 'b', 'c', 'b', 'c', 'd']
arr -= ['a', 'c']
# ['b', 'b', 'd']
There are several equivalent of the _.pull, but they work with one value at a time.
You could use
[:a, :b, :c, :a, :b].delete(:a) # => can't accept multiple arguments
Or you could use
[:a, :b, :c, :a, :b].delete_if{|x| [:a, :b].include? x}
Or you could use #andrunix approach above by subtracting the array:
arr = ['a', 'b', 'c', 'b', 'c', 'd']
arr -= ['a', 'c']

Stuck on creating a function that will return objects

Hi I was wondering if I can get some help here I am stuck. I am trying to create a histogram function that takes an array like ['a', 'a', 'b', 'c', 'b', 'a'] and returns {a:3, b:2, c:1} using the reduce function to build the histogram function. But I am stuck on what the callback function should be.
Thank you for any responses.
You can reduce your array like this:
['a', 'a', 'b', 'c', 'b', 'a'].reduce(function(obj, value) {
obj[value] = obj[value] || 0;
obj[value]++;
return obj;
}, {});
If your environment supports Object.assign() and ES6 you can also do this:
['a', 'a', 'b', 'c', 'b', 'a']
.reduce((a, b) => Object.assign(a, {[b]: a[b] ? ++a[b] : 1}), {});
Try to iterate over the array and fill/increment the object's value accordingly,
var x = ['a', 'a', 'b', 'c', 'b', 'a'];
var y = {};
x.forEach(function(itm){
y[itm] = ++y[itm] || 1;
});

Categories

Resources