Immutable.js nested array updateIn - javascript

I want to make use of Immutable's functions to update my structure from this structure but I'm uncertain in how to do it. My tags shouldn't be an array but should become objects just for posting to my REST api.
There are several examples on how to update deep properties and nested arrays, but I want to change values 2 arrays deep.
order: {
createDate: '2016-08-07',
pickupDate: '2017-02-02',
items: [
{
price: '25',
tags: [
'Blue',
'Pants'
]
}
]
}
to this structure
order: {
createDate: '2016-08-07',
pickupDate: '2017-02-02',
items: [
{
price: '25',
tags: [
{
text: 'Blue'
},
{
text: 'Pants'
}
]
}
]
}

Something like this:
const { fromJS, Map } = require("immutable")
const obj = fromJS({
order: {
createDate: '2016-08-07',
pickupDate: '2017-02-02',
items: [
{
price: '25',
tags: [
'Blue',
'Pants'
]
}
]
}
})
const items = obj
.getIn(['order', 'items'])
.map((item) => {
const newTags = item
.get('tags')
.map((tag) => ({ text: tag }));
return item.set('tags', newTags);
});
const newMap = obj.setIn(['order', 'items'], items);

Related

Advanced filtering nested elements in TypeScript/JavaScript

Given the following structure and data:
interface GrandChild {
id: number,
values: Array<string>,
}
interface Child {
id: number,
subItems: Array<GrandChild>
}
interface Foo {
items: Array<Child>
}
const data: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: ['REMOVE', 'REMOVE'] }, { id: 21, values: ['REMOVE'] } ] },
{ id: 3, subItems: [ { id: 30, values: ['REMOVE'] }, { id: 31, values: ['REMOVE'] }, { id: 32, values: ['REMOVE', '32'] } ] },
]
};
How can I use the Array's methods (filter, map, some, etc.) to achieve the following result?
const expected: Foo = {
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 3, subItems: [ { id: 32, values: ['32'] } ] },
]
}
So far, I filtered the resulting data, removing the undesired elements, as following:
const filteredData: Foo = {
...data,
items: data.items.map(item => ({
...item,
subItems: item.subItems.map(subItem => ({
...subItem,
values: subItem.values.filter(value => value !== 'REMOVE')
}))
}))
}
Resulting:
{
items: [
{ id: 1, subItems: [ { id: 10, values: ['10', '100'] }, { id: 11, values: ['11', '110', '1100'] } ] },
{ id: 2, subItems: [ { id: 20, values: [] }, { id: 21, values: [] } ] },
{ id: 3, subItems: [ { id: 30, values: [] }, { id: 31, values: [] }, { id: 32, values: ['32'] } ] },
]
};
But, I cannot figure a way out to remove the empty subItems elements without looping through the result.
You can check online the above code here.
If you really want to do it just with filter and map, add a filter after each of your maps to remove subItems that have an empty values array and to remove items that have an empty subItems array:
const filteredData = {
...data,
items: data.items
.map((item) => ({
...item,
subItems: item.subItems
.map((subItem) => ({
...subItem,
values: subItem.values.filter((value) => value !== "REMOVE"),
}))
.filter(({ values }) => values.length > 0), // ***
}))
.filter(({subItems}) => subItems.length > 0), // ***
};
But:
When I have map followed by filter, I always ask myself if the data is large enough that I should avoid making multiple passes through it.
When I'm doing lots of nesting of map calls and such, I always ask myself if it would be clearer when reading the code later to use simpler, smaller loops.
Here's what you might do if answering "yes" to either or both of those questions:
const filteredData: Foo = {
...data,
items: [],
};
for (const item of data.items) {
const subItems: Array<GrandChild> = [];
for (const subItem of item.subItems) {
const values = subItem.values.filter((value) => value !== "REMOVE");
if (values.length) {
subItems.push({
...subItem,
values,
});
}
}
if (subItems.length > 0) {
filteredData.items.push({
...item,
subItems,
});
}
}

JavaScript Array Filtering in Nested Arrays

I have an array that looks something like this:
const arrayObj = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [
{
name: "Jack",
},
{
name: "Man",
},
],
},
]
What I want is to filter the objects whose itemsList contain an object with the name of a certain value. For example, I want to be able to filter out an array with objects whose inner objects with names that contain "ul" (in this case the name Paul contains "ul"), it should give me an output as such:
const outputArray = [
{
id: 1,
itemsList: [
{
name: "Paul",
},
{
name: "Newman",
},
]
}
]
So far, I've only been able to filter out a simple flat array of objects with this function:
function filterByName(array: any, string: any) {
return array.filter((obj: any) =>
["name"].some((key: any) =>
String(obj[key]).toLowerCase().includes(string.toLowerCase())
)
);
}
but I don't know how to apply it to my case.
Here you can use the some method combined with the includes method
const arrayObj = [{
id: 1,
itemsList: [{
name: "Paul",
},
{
name: "Newman",
},
],
},
{
id: 2,
itemsList: [{
name: "Jack",
},
{
name: "Man",
},
],
},
]
const getFilterArray = (name) => {
return arrayObj.filter(obj => obj.itemsList.some(x => x.name.toLowerCase().includes(name.toLowerCase())))
}
console.log(getFilterArray("ul"))
const result = arrayObj.filter(({ itemsList }) =>
itemsList.some(({ name }) => name.toLowerCase().includes('ul')));
Can you try this?

Recursivly change object nested property name

I have object with nested fields than named subItem I need to change names of this field to children.
How can this functionality be neatly implemented, perhaps there are ways besides recursion?
let data = [
{
name: 'Alex',
subItem: [
{
name: 'Den',
subItem: [
]
}
]
}
]
function transformData = (data) => {
return data.map(stat=>{
})
}
console.log(transformData(data))
//shoud be
let data = [
{
name: 'Alex',
children: [
{
name: 'Den',
children: []
}
]
}
]
Javascript already includes a recursive object transformer, JSON. stringify with replacer function:
let data = [
{
name: 'Alex',
subItem: [
{
name: 'Den',
subItem: [ {name: 'Foo', subItem: []} ],
}
]
}
]
JSON.stringify(data, function(_, v) {
if (v.subItem) {
v.children = v.subItem
delete v.subItem
}
return v
})
console.log(data)
In the simple case like this, you can also just manipulate the JSON string directly:
newData = JSON.parse(
JSON.stringify(data)
.replaceAll(`"subItem":`, `"children":`))

How would you change values/add to nested object data inside array of objects using Javascript?

const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
fan.teams.map(team => {
team.beers.map(beer => beers.filter(brand => brand.id === beer)[0])
});
});
Above is a sample I put together to best demonstrate my question. I am having trouble understanding why nested mapping (.map()) of object arrays is not allowing me to alter the nested data. When I console log results, I am either getting an "[undefined, undefined]' or the unchanged "people" array.
I would like to return the same array as "people" except replace the nested "beers" array (people.teams.beers[]) with corresponding objects from the "beers" array. Example of a successful result below:
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: [
{
id: '100',
name: 'stoneys'
}
]
},
{
name: 'penguins',
beers: [
{
id: '300',
name: 'miller'
}
]
}
]
}
Array.map expects a function which takes single array element as parameter and returns a mapped value. In your case you're not returning any value from mapping functions therefore you're getting undefined twice
const beers = [
{
id: '100',
name: 'stoneys'
},
{
id: '200',
name: 'budweiser'
},
{
id: '300',
name: 'miller'
},
{
id: '400',
name: 'corona'
}
];
const people = [
{
name: 'steve',
teams: [
{
name: 'pirates',
beers: ['100']
},
{
name: 'penguins',
beers: ['300']
}
]
},
{
name: 'jim',
teams: [
{
name: 'indians',
beers: ['200']
},
{
name: 'blue jackets',
beers: ['100', '400']
}
]
}
];
let newPeople = people.map(fan => {
let teams = fan.teams.map(team => {
let beer = team.beers.map(beer => beers.filter(brand => brand.id === beer)[0]);
return { name: team.name, beers: beer }
});
return { name: fan.name, teams: teams }
});
console.log(newPeople);

double nested array of object es6 filter

I want to filter out a nested array of objects but stuck at the filter part.
How to remove one of the mark?
this.state = {
data: [
{
id: 1,
name: "Main",
subs: [
{
id: "jay",
name: "Jay",
mark: [
{
id: "5a5d84b94a074c49ef2d4553",
name: 100
},
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}
]
}
]
};
https://codesandbox.io/s/p39momxzp7
I try to use es6 as it's more readable.
expected output
data: [
{
id: 1,
name: "Main",
subs: [
{
id: "jay",
name: "Jay",
mark: [
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}
]
}
]
Since there are multiple nested arrays in your data structure, you need to use forEach those many times
data.forEach( s => //iterate data
s.subs.forEach( t => //iterate subs
( t.mark = t.mark.slice( 1, 2 ) ) ) ); //slice the second value out
Demo
var data = [{
id: 1,
name: "Main",
subs: [{
id: "jay",
name: "Jay",
mark: [{
id: "5a5d84b94a074c49ef2d4553",
name: 100
},
{
id: "5a5d84b94a074119ef2d4553",
name: 70
}
]
}]
}];
data.forEach(s => s.subs.forEach(t => (t.mark = t.mark.slice(1,2))));
console.log(JSON.stringify(data, 0, 4))
In case the last value should be picked?
data.forEach( s => //iterate data
s.subs.forEach( t => //iterate subs
( t.mark = t.mark.slice( -1 ) ) ) ); //slice the last value out
If you are trying to filter a relevant mark by a given id,
you can combine Array#map and Array#filter to achieve it:
Note that i'm also using the Object Rest/Spread Properties proposal (stage 4)
Running example
const state = {
data: [{
id: 1,
name: "Main",
subs: [{
id: "jay",
name: "Jay",
mark: [{
id: "5a5d84b94a074c49ef2d4553",
name: 100
}, {
id: "5a5d84b94a074119ef2d4553",
name: 70
}]
}]
}]
};
const mark_id = '5a5d84b94a074119ef2d4553';
const nextState = {
...state,
data: state.data.map(obj => {
const filteredSubs = obj.subs.map(sub => {
const markById = sub.mark.filter(m => m.id === mark_id);
return {
...sub,
mark: markById
}
});
return {
...obj,
subs: filteredSubs
}
})
};
console.log(nextState);
You can even use lodash which contains many methods that can be handled easily.
Check if this is what you are looking for. (there is a good scope to refactor it but before that would like to understand if thats what you are looking for)
Below is the code that has been used there.
let inputId = "5a5d84b94a074c49ef2d4553";
let filteredData =_.each(_.cloneDeep(data), function(value, key1) {
_.each(value.subs, function(valueSubs, key2) {
var finalSubMark = _.find(valueSubs.mark, function(eachMark) {
return eachMark.id == inputId;
});
_.set(valueSubs, "mark", finalSubMark);
});
});
https://codesandbox.io/s/v065w05rly

Categories