Functional immutable way in javascript to copy an array with additional items in certain positions depending on condition on items - javascript

I have an array:
[
{ "name": "batman", "hasSidekick": true },
{ "name": "shazam!", "hasSidekick": false },
{ "name": "capt america", "hasSidekick": true },
{ "name": "spiderman", "hasSidekick": false }
]
From this, I want to create a new array of hero names which will have all of the above names but when hasSidekick is true for a hero, there should be an additional name inserted after it.
Expected output:
[
"batman",
"batman's sidekick",
"shazam!", ,
"capt america",
"capt america's sidekick",
"spiderman"
]
I can do it with forEach and pushing additional items conditionally based on hasSidekick:
const heroes = [
{ name: "batman", hasSidekick: true },
{ name: "shazam!", hasSidekick: false },
{ name: "capt america", hasSidekick: true },
{ name: "spiderman", hasSidekick: false },
];
let heroesAndSidekicks = [];
heroes.forEach(hero => {
heroesAndSidekicks.push(hero.name);
if (hero.hasSidekick) {
heroesAndSidekicks.push(`${hero.name}'s sidekick`);
}
});
console.log(heroesAndSidekicks);
But please suggest how I can do it in functional programming way without mutation.

You could take Array#flatMap.
var data = [{ name: "batman", hasSidekick: true }, { name: "shazam!", hasSidekick: false }, { name: "capt america", hasSidekick: true }, { name: "spiderman", hasSidekick: false }],
result = data.flatMap(({ name, hasSidekick }) => hasSidekick
? [name, name + '\'s sidekick']
: name
);
console.log(result);

I think Array.prototype.reduce() can solve your issue. From the documentation:
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Please find a possible solution below:
const data = [
{ "name": "batman", "hasSidekick": true },
{ "name": "shazam!", "hasSidekick": false },
{ "name": "capt america", "hasSidekick": true },
{ "name": "spiderman", "hasSidekick": false }
];
const result = data.reduce((a, e) => {
a.push(e.name);
if (e.hasSidekick) {
a.push(`${e.name}'s sidekick`);
}
return a;
}, []);
console.log(result);
I hope that helps!

Related

how to make nested array objects in javascript in a key value pair format

array data=[
{
"id":1,
"name":"john",
"income":22000,
"expenses":15000
},
{
"id":2,
"name":"kiran",
"income":27000,
"expenses":13000
},
{
"id":1,
"name":"john",
"income":35000,
"expenses":24000
}
]
i want to make a new array set in following format which is in a key value pair. ie result set.
can you please explain the best method. ? how to achive using foreach.?
tried using foreach method by looping each element. but cant get the desired output format
var result= [ {
"name": "john",
"series": [
{
"name": "income",
"value": 22000
},
{
"name": "expenses",
"value": 15000
},
]
},
{
"name": "kiran",
"series": [
{
"name": "income",
"value": 27000
},
{
"name": "expenses",
"value": 13000
},
]
}]
// Your array
const result = [
{
name: "john",
series: [
{
name: "income",
value: 22000,
},
{
name: "expenses",
value: 15000,
},
],
},
{
name: "kiran",
series: [
{
name: "income",
value: 27000,
},
{
name: "expenses",
value: 13000,
},
],
},
];
// What is .map function?
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
// Output
// map return a new function.
// it's a loop method but more equipped
result.map((item, index) => {
const seriesKeyValues = {};
// forEach is too, it's a loop method.
// but not have a return value,
// just loops and give you item on each loop
item.series.forEach(serie => {
//seriesKeyValues is a object.
// different between seriesKeyValues.serie.name
// it's a bracket notation
// look this documentation
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#computed_property_names
seriesKeyValues[serie.name] = serie.value;
});
// return new Object
// ... is 'spread syntax' basically combine objects
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#spread_properties
// spread syntax is a new way.
// old way is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
return {
id: index,
name: item.name,
...seriesKeyValues,
};
});
I hope it will help :). if you don't understand any lines of code, i can explain

Filtering objects array by nested values

I'm trying to filter this objects array and keep the original one aside.
{"departments":
[
{
“name": “AAA",
“selected”: true,
"courses": [
{
"name": “course1",
“selected”: true,
“titles”:
[{
"name": “title1",
“selected”: true
},
{
"name": “title2",
“selected”: false
}]
},
{
"name": “course2",
“selected”: false,
“titles”:
[{
"name": “title1",
“selected”: false
}]
}
]
},
{
“name": “BBB",
“selected”: false,
"courses": [{...}]
{...}
]
}
I want to find all the selected departments, courses and titles. And it should be in the same format.
I tried with below code, but it change original data. I want to keep that aside too.
const depts = departments.filter((dept: any) => {
if (dept.selected) {
dept.courses = dept.courses.filter((course: any) => {
if (course.selected) {
if (course.titles) {
course.titles = course.titles.filter(({selected}: any) => selected);
}
return true;
}
return false;
});
return true;
}
return false;
});
What would be considered the best solution in this case?
Shorter alternative can be to use the JSON.parse reviver parameter :
var arr = [{ name: "AAA", selected: true, courses: [{name: "course1", selected: true, titles: [{ name: "title1", selected: true }, { name: "title1", selected: false }]}, { name: "course2", selected: false, titles: [{ name: "title1", selected: false }]}]}]
var result = JSON.parse(JSON.stringify(arr), (k, v) => v.map ? v.filter(x => x.selected) : v)
console.log( result )
your filtering logic seems to be correct. only problem is that code changes original array. in order to overcome this problem just create a deep clone of original array and run filtering logic on it
filterArray() {
const clone = JSON.parse(JSON.stringify(this.departments));
const depts = clone.filter((dept: any) => {
if (dept.selected) {
dept.courses = dept.courses.filter((course: any) => {
if (course.selected) {
if (course.titles) {
course.titles = course.titles.filter(({ selected }: any) => selected);
}
return true;
}
return false;
});
return true;
}
return false;
});
console.log(depts);
}
here is a demo https://stackblitz.com/edit/angular-xx1kp4
const filterSelected = obj => {
return {
...obj,
departments: obj.departments.map(dep => {
return {
...dep,
courses: dep.courses.map(course => {
return {
...course,
titles: course.titles.filter(title => title.selected),
};
}).filter(course => course.selected),
};
}).filter(dep => dep.selected),
};
}
const all = {
departments: [
{
name: "AAA",
selected: true,
courses: [
{
name: "course1",
selected: true,
titles: [
{
name: "title1",
selected: true
}, {
name: "title1",
selected: false
}
]
}, {
name: "course2",
selected: false,
titles: [
{
name: "title1",
selected: false
}
]
},
]
}
]
};
console.log(filterSelected(all));
I don't know if you prefer an API false. Here is my tip:
You can to use an API Json Server.
Install JSON Server
npm install -g json-server
Create a db.json file with some data
{
"posts": [
{ "id": 1, "title": "json-server", "author": "typicode" }
],
"comments": [
{ "id": 1, "body": "some comment", "postId": 1 }
],
"profile": { "name": "typicode" }
}
Start JSON Server
json-server --watch db.json
Now if you go to http://localhost:3000/posts/1, you'll get
{ "id": 1, "title": "json-server", "author": "typicode" }
you can search your array of objects using various shapes and it will come filtered. More about the API here: https://github.com/typicode/json-server
(Use a filter to do your searches on the Angular, it will bring you right what you need, use a method inside your component)

How to filter an array of objects based on another array of objects?

I have two array of objects like below
conditions= [
{
'condition': 'Expert',
'value': 'All'
},
{
'condition': 'Coach',
'value': 'willaim'
},
{
'condition': 'manager',
'value': 'Brandy Lovings'
},
{
'condition': 'site',
'value': 'ALL'
},
{
'condition': 'client',
'value': 'ALL'
}
]
data=[
{
"Date": "11/6/2018",
"client": "Verizon",
"Expert": "Ellison, Lauren",
"Coach": "willaim",
"manager": "Brandy Lovings",
"site": "Sundance",
"Metric": "STR"
},
{
"Date": "11/6/2018",
"client": "Amzaon",
"Expert": "Ellison, Lauren",
"Coach": "Dash Williamson",
"manager": "David",
"site": "abc",
"Metric": "STR"
}
]
I want to filter data array with the conditions array, like if condition property in conditions array contain Expert then I need to filter data array based on data.Expert = conditions[Expert Conditionindex].value then I need to return all the data with this conditions.
Another thing is, If value: 'ALL' then no need of filtering in that particular condition.
The desired output is like
filteredData = [
{
"Date": "11/6/2018",
"client": "Verizon",
"Expert": "Ellison, Lauren",
"Coach": "willaim",
"manager": "Brandy Lovings",
"site": "Sundance",
"Metric": "STR"
}
]
How do I solve this problem?
You could filter with a subset of conditions without ALL flag.
var conditions = [{ condition: "Expert", value: "All" }, { condition: "Coach", value: "willaim" }, { condition: "manager", value: "Brandy Lovings" }, { condition: "site", value: "ALL" }, { condition: "client", value: "ALL" }],
data = [{ Date: "11/6/2018", client: "Verizon", Expert: "Ellison, Lauren", Coach: "willaim", manager: "Brandy Lovings", site: "Sundance", Metric: "STR" }, { Date: "11/6/2018", client: "Amzaon", Expert: "Ellison, Lauren", Coach: "Dash Williamson", manager: "David", site: "abc", Metric: "STR" }],
filters = conditions.filter(({ value }) => value.toUpperCase() !== 'ALL'),
result = data.filter(o =>
filters.every(({ condition, value }) => o[condition] === value));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This should work for you:
const conditionsObj={}
conditions.filter(({value})=>value.toLowerCase()!=='all').forEach((condition)=>{
conditionsObj[condition.condition]=condition.value
})
const results=data.filter((item)=>{
let match=false;
Object.keys(conditionsObj).forEach((_key)=>{
if(conditionsObj[_key]===item[_key]){
match=true;
}
})
return match;
})
console.log(results)

How to filter object in one array using propery in another in javascript?

First Array
const firstArray = [
{
"value": "306",
"label": "All"
},
{
"value": "316",
"label": "Tips"
},
{
"value": "315",
"label": "News"
},
{
"value": "32",
"label": "Jobs"
}]
Second Array
const secondArray = [
{
name: "name",
description: "desc",
image: "path_image",
culture: [{
name: "name",
value: "32"
}]
},
{
name: "name",
description: "desc",
image: "path_image",
culture: [{
name: "name",
value: "32"
}]
}];
Trying to filter my firstArray with only keeping the object with the value corresponding to 32.
Still learning Javascript and it's in a React Native Project. They changed some info of the API and it was working when I only had : culture":"32"
Code :
let newOrigin = [...new Set(this.state.secondArray.map(product => product.culture))],
visibleOrigin = firstArray.filter(item => newOrigin.includes(item.value));
this.setState({ displayOrigin: visibleOrigin });
How to get the value inside the array culture.
Any advice, any help ? Thank you.
So. I found a solution to my issue. Here's the code :
_nameFunction = () => {
let filteredSecondArray = [...new Set(this.state.secondArray.map(o => o.culture[0].value))];
this.setState({
firstArray: this.state.firstArray.filter(item => filteredSecondArray.includes(item.value))
})
}

javascript optimize multiple reduce and map to extract data from nested objects

I am extracting some data out of an array of nested objects, using two reducees, and map, which is working at the moment, but it is a bit ugly. How can this be optimized?
function extractSchools(schools) {
let schoolData = [];
if (schools) {
schoolData = schools.reduce(function(parentdata, chlrn) {
let childrenlist = chlrn.children;
let childrendata = [];
if (childrenlist) {
childrendata = childrenlist.reduce(function(addrsslist, school) {
return addrsslist.concat(school.address.map(i => i.school));
}, []);
}
return parentdata.concat(chlrn.parent, childrendata);
}, []);
}
return {
schoolData
};
}
const schools = [{
"parent": "Thomas Jefferson",
"children": [{
"address": [{
"school": "School A"
}]
},
{
"address": [{
"school": "School B"
}]
}
]
},
{
"parent": "Jack Chan",
"children": [{
"address": [{
"school": "School C"
}]
}]
}
];
console.log(extractSchools(schools));
How can I optimize this function to get the same results? using one reduce instead of two... or some other optimal way of doing it.
You can remove the if (childrenlist) { and use a pre-filter.
function extractSchools(schools) {
let schoolData = [];
if (schools) {
schoolData = schools
.filter(data => data.children)
.reduce((parentdata, chlrn) => {
const childrendata = chlrn.children.reduce(
(addrsslist, school) =>
addrsslist.concat(school.address.map(i => i.school)),
[]
);
return parentdata.concat(chlrn.parent, childrendata);
}, []);
}
return { schoolData };
}
const schools = [
{
parent: "Thomas Jefferson",
children: [
{
address: [
{
school: "School A"
}
]
},
{
address: [
{
school: "School B"
}
]
}
]
},
{
parent: "Jack Chan",
children: [
{
address: [
{
school: "School C"
}
]
}
]
}
];
console.log(extractSchools(schools));
Try this, the result is little different than what you are expecting, but this will be a more generic way where you will have addresses with respect to school.
schools.map(p => {
return {[p.parent]: p.children.map(c => c.address.map(add => add.school))}
})
[
{
"Thomas Jefferson": [
[
"School A"
],
[
"School B"
]
]
},
{
"Jack Chan": [
[
"School C"
]
]
}
]

Categories

Resources