Immutable.fromJS() is not deeply converting my JSON to Immutable object - javascript

I have 2 given immutable lists and I am concatenating them as follows:
const fruits = Immutable.List(['apple', 'banana']);
const vegetables = Immutable.List(['carrot']);
const groceries = fruits.concat(vegetables);
I used map function to transform data to get list of objects and finally converted it to immutable using fromJS like below:
const result = groceries.map((item, index) => ({ id: index, item }));
const checkList = Immutable.fromJS({ groceries: result });
So the final data is the immutable object of the given JSON:
{
"groceries": [
{
"id": 0,
"item": "apple"
},
{
"id": 1,
"item": "banana"
},
{
"id": 2,
"item": "carrot"
}
]
}
I expect fromJS would deeply convert the object { groceries: result } to immutable object. But when I check the value of checkList.getIn(['groceries', '0']) I get a plain JSON object {id: 0, item: 'apple`} instead of the expected immutable map.
Can someone help me out why this happens

Quoting the statements in immutableJS repo
Immutable.fromJS() is conservative in its conversion. It only converts plain Objects (no custom prototype) to Immutable.Map and true Arrays to Immutable.List. This ensures that exotic objects (Date objects, DOM nodes, user-defined types) don't get converted to Immutable.Map unintentionally.
So it converts only plain Objects. In your case before fromJS is used the data looks like below:
{
groceries: <Immutable List>
}
And within the list, each element is stored as plain JSON object like below:
{ id: 0, item: 'apple'}
fromJS converts your data to immutable until it encounters anything other than plain Objects and true Arrays. That's why it didn't convert the elements in the immutable list as it is not true Array.
Your problem could be solved by adding fromJS to the below code
const result = groceries.map((item, index) => fromJS({ id: index, item }));

Related

Access and extract values from doubly nested array of objects in JavaScript

I have an array of objects and within those objects is another object which contains a particular property which I want to get the value from and store in a separate array.
How do I access and store the value from the name property from the data structure below:
pokemon:Object
abilities:Array[2]
0:Object
ability:Object
name:"blaze"
1:Object
ability:Object
name:"solar-power"
How would I return and display the values in the name property as a nice string like
blaze, solar-power ?
I tried doing something like this but I still get an array and I don't want to do a 3rd loop since that is not performant.
let pokemonAbilities = [];
let test = pokemon.abilities.map((poke) =>
Object.fromEntries(
Object.entries(poke).map(([a, b]) => [a, Object.values(b)[0]])
)
);
test.map((t) => pokemonAbilities.push(t.ability));
Sample Data:
"pokemon": {
"abilities": [
{
"ability": {
"name": "friend-guard",
"url": "https://pokeapi.co/api/v2/ability/132/"
},
"ability": {
"name": "Solar-flare",
"url": "https://pokeapi.co/api/v2/ability/132/"
}
}
]
}
Then I am doing a join on the returned array from above to get a formatted string.
It just seems like the multiple map() loops can be optimized but I am unsure how to make it more efficient.
Thank you.
There is no need for a loop within loop. Try this:
const pokemon = {
abilities: [{
ability: {
name: 'friend-guard',
url: 'https://pokeapi.co/api/v2/ability/132/'
},
}, {
ability: {
name: 'Solar-flare',
url: 'https://pokeapi.co/api/v2/ability/132/'
}
}]
};
const pokemonAbilities = pokemon.abilities.map(item => item.ability.name).join(', ');
console.log(pokemonAbilities);

Alternative to eval for converting a string to an object

I have a function that is using eval to convert a string with an expression to an object based on the parameter.
let indexType = ["Mac", "User", "Line", "Mask", "Ip", "Location"]
const filterIndex = (item) => {
filteredIndexSearch = []
eval(`search${item}`).forEach((e) => filteredIndexSearch.push(searchData[e.key]))
}
filterIndex(indexType[searchTotal.indexOf(Math.max(...searchTotal))])
searchData is an array that returns values based on the user input.
searchTotal is an array with the length of each search{item} array.
The filterIndex function takes the highest value from the searchData array and corresponds it to the indexType array, then use eval to convert the string to an object to pass the value to the filteredIndexSearch array.
What would be a better alternative to eval?
EDIT
To add more information on what this does:
searchData = [
[
{
key: 1,
data: "0123456789101"
},
{
key: 1,
data: "John Smith"
}
],
[
{
key: 2,
data: "0123456789102"
},
{
key: 2,
data: "Jane Smith"
},
]
]
const search = (data, key, container) => {
if (!data) data = "";
if (data.toLowerCase().includes(string)) {
container = container[container.length] = {
key: key,
data: data
}
}
}
const returnSearch = () => {
for (let i = 0; i < searchData.length; i++) {
search(searchData[i][0].data, searchData[i][0].key, searchMac)
search(searchData[i][1].data, searchData[i][1].key, searchUser)
}
}
returnSearch()
The data is incomplete, but hopefully conveys what I'm trying to do.
search will take the user input, and store the information in the corresponding array. If I input "Jo", it will return the searchUser array with only the "John Smith" value and all the other values with the same key. Inputting "102" returns the searchMac with the "0123456789102" value and all other values with the same key.
At the end of the day. I just want to convert search${parameter} to an object without using eval.
Move your global arrays into an object.
Somewhere it appears that you're defining the arrays, something like:
searchMac = [...];
searchUser = [...];
...
Instead of defining them as individual arrays, I'd define them as properties in an object:
searchIndices.Mac = [...];
searchIndices.User = [...];
...
Then, instead of using eval, your can replace your eval().forEach with searchIndices[item].forEach.
If the order of your search isn't important, your can instead loop through the keys of searchIndices:
Object.keys(searchIndices).forEach(item => {
searchIndices[item].forEach(...);
});
This ensures that if you ever add or drop an entry in searchIndices, you won't miss it or accidentally error out on an undefined search index.
Any time you have a situation with variables named x0, x1 etc, that should be a red flag to tell you you should be using an array instead. Variable names should never be semantically meaningful - that is code should never rely on the name of a variable to determine how the code behaves. Convert search0 etc into an array of search terms. Then use:
const filterIndex = (item) => search[item].map(i => searchData[i.key]);
filteredIndexSearch = filterIndex(indexType[searchTotal.indexOf(Math.max(...searchTotal))]);
(simplifying your code). Note that in your code, filteredIndexSearch is modified inside the arrow function. Better to have it return the result as above.

From an array of objects, extract value of properties for each object and put in a different array

I have a group of filters that is an Reactive Forms Object. I’ve taken the property values of the object and pushed it into an array.
// original filters object {claim_number: null, status: "Approved", patient: null, service_date: null}
let filterArr = []
Object.keys(this.filtersForm.value).forEach(filter => {
filterArr.push(this.filtersForm.value[filter])
// filterArr [null, “Approved, null, null]
})
I have a table that is comprised of an array of objects like the following:
"claims":[
{
"billed_amount":141.78,
"claim_number": "6596594-0",
"location":"University Hospital",
"member_id":"A1234567890",
"status":{
"label":"Approved",
"value": "Approved"
}
},
{
"billed_amount":341.70,
"claim_number": "2196524-3",
"location":"Springfield Hospital",
"member_id":"B1234567890",
"status":{
"label":"Pending",
"value":"Pending"
}
},
{
"billed_amount":111.70,
"claim_number": "1233514-5",
"location":"Springfield Hospital",
"member_id":"C1234567890",
"status":{
"label":"Pending",
"value":"Pending"
}
},
{
// ...etc
}
]
I am trying to loop through each row and put the property values in an array, one for each row so I can filter them against filterArr. How can I do that?
My question is similar to this post (From an array of objects, extract value of a property as array
), with the key difference being that I'm trying to create an array per object.
Each object represents a row in a table that I am trying to dynamically filter. So I can't have values from different rows being put into one array.
According to your desired result, I think you can use ES6 functions.
const result = yourTable.map(element => Object.values(element));
Using map() function, you go through all elements, and extract from each object its values.
Unsure what you want to include in your output but the below will loop through an array and return an array to the filter function
const output = claimTable["claims"].map((claim) => {
return claim
}).filter((claim) => {
return claim.billed_amount > 100
})
The above will loop through the claims and 'convert' to an array. The filter will return that claim for all true conditions (in this case, if the billed amount is greater than 100).
This article goes over this and adds a bit more to it.

Filter a javascript tree without mutating the original array

I'm working on a react project where I need to filter an array of objects without mutating the original array
const array = [{
name: 'bar',
children: [{
name: 'foo',
children: [{
name: 'baz123',
}, {
name: 'baz',
}]
}]
}, {
name: 'shallowKey'
}, {
name: 'abc'
}];
For example, I want to only filter the concerned object and its children.
This is the jsfiddle
function filterData(data) {
var r = data.filter(function(o) {
if (o.children) o.children = filterData(o.children);
return o.name.length === 3;
})
return r;
}
I tried that function from a stackoverflow question, but is there a way to use that same functionality without mutating the data. Thanks
If you don't have any prototypes or functions involved within the objects a simple way to copy is to stringify original and parse it back to object
var r= JSON.parse(JSON.stringify(data)).filter(...
Array .filter() already creates a new array, you just need to fix the part where the o.children is mutated. To do that you could use .map() and simply copy all fields using Object.assign() or object spread and just assign children as the result passed through the same filter function:
function filterData(data) {
return data
.filter(obj => obj.name.length === 3) // filter array first
.map(obj => ({ // then re-map to new objects
...obj, // copy shallow fields
children: obj.children && filterData(obj.children) // filter children
}));
}
You can create a copy of your original array using a spread operator or Object.assign() function.
const arrayCopy= [...array] //spread operator
const arrayCopy = Object.assign({}, array);
Otherwise as Aaron suggested, using filter(), map(), reduce() function always returns a new array without mutating your original array.

Array filteration and Extraction of data and append to new Array

I have an array with nested array
I want the data to append in a new array.
For the data extraction or filtration what method's i have to use, using library such as lodash
DATA
[
[
{
_id: 588d9b8a608f2a66c298849f,
email: 'sd#',
password: '$2a$10$6..L3c3tANi6ydt9gZbc1O6prPfUd3RB.ner5lilxRyEwo1lPsSoC',
isJobSeeker: true,
__v: 0,
lastName: 'shrestha',
firstName: 'manish',
isSeeker: true
}
],
[
{
_id: 588dbb4f7a48ce0d26cb99fd,
jobId: [Object],
seekerId: 588d9b8a608f2a66c298849f,
employerId: 588d7d6c0ec4512feb819825,
__v: 0,
}
]
]
REQUIRED DATA
[
{
_id: 588d9b8a608f2a66c298849f,
email: 'sd#',
password: '$2a$10$6..L3c3tANi6ydt9gZbc1O6prPfUd3RB.ner5lilxRyEwo1lPsSoC',
isJobSeeker: true,
__v: 0,
lastName: 'shrestha',
firstName: 'manish',
isSeeker: true
},
jobId: [{}, {}, {}] // ARRAY WITH OBJECTS
]
also i want to change the jobId key to other key of custom string as jobs
Following is my attempt:
console.log('Data filteration', data);
const filteredData = [];
filteredData.push(data[0][0]);
data[1].forEach((i) => {
filteredData[0].jobs = i.jobId
});
console.log('filteredData', filteredData);
First you should clean you data to have a better structure.
[
[
{ ... }
],
[
{ ... }
]
]
In this datastructure, its difficult to understand what does inner arrays signify. Instead you should use an object. That would define the purpose of array and make your code more readable.
var data=[[{_id:"588d9b8a608f2a66c298849f",email:"sd#",password:"$2a$10$6..L3c3tANi6ydt9gZbc1O6prPfUd3RB.ner5lilxRyEwo1lPsSoC",isJobSeeker:!0,__v:0,lastName:"shrestha",firstName:"manish",isSeeker:!0}],[{_id:"588dbb4f7a48ce0d26cb99fd",jobId:["test","test1"],seekerId:"588d9b8a608f2a66c298849f",employerId:"588d7d6c0ec4512feb819825",__v:0}]];
var cleanedData = {
userData: data[0],
userJobMap: data[1],
}
var result = cleanedData.userData.reduce(function(p,c){
if(c.isJobSeeker){
var job = cleanedData.userJobMap.filter(x=> x.seekerId === c._id);
// To copy object and not reference
var t = Object.assign({}, c, { jobId: job[0].jobId });
p.push(t)
}
return p
}, [])
console.log(result)
References
Array.map is a tool that iterates over all elements and return different value say a single property of return double value of all numbers in array. Note, this will yield an array of same size.
Array.filter on the other hand is use to filter array based on condition. This will return a subset of original data but elements will be same. You cannot change element structure.
Array.reduce is a tool that address cases where you need to return selected elements with parsed value. You can achieve same by chaining .filter().map() but then its an overkill as it would result in O(2n).
Object.assign In JS objects are passed by reference. So if you assign an object to a variable, you are not copying entire object, but only reference. So it you change anything in this variable, it will also reflect in original object. To avoid this, you need to copy value. This is where Object.assign comes. Note, its not supported by old browsers. For them you can check following post - What is the most efficient way to deep clone an object in JavaScript?
Note: All array functions are part of functional programming paradigm and are used to make your code more readable and concise but they come at an expense of performance. Traditional for will always perform faster then them. So if you want to focus on performance, always try to use for (though difference is very small but can add up for multiple cases and become substantial)

Categories

Resources