ImmutableJS - pushing into a nested array - javascript

const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{ id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.updateIn([0, 'subList'], function (items) {
return items.concat(newItem)
});
https://jsbin.com/midimupire/edit?html,js,console
Results in:
Error: invalid keyPath
I think that perhaps I need to set subList as a List(); I get the same error when trying this.

If I understand the question correctly, you want to return collection with the first element as:
{
id : 1,
subList: [
{name: "sublist item"}
]
}
To do this we'll need to make a few changes.
Use Immutable.fromJS to deeply convert the plain JS array of objects to an Immutable List of Maps
Use List.update() to return a new List with the updated value
Use Map.updateIn() to return a new LMapist with the updated value
Here's the whole thing:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.update(0, item => {
return item.updateIn(['subList'], subList => {
return subList.concat(newItem);
});
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>
And the result:
[
{
"id": 1,
"subList": [
{
"name": "sublist item"
}
]
},
{
"id": 2,
"subList": []
},
{
"id": 3,
"subList": []
}
]
Update: List.updateIn() can use an index as the keypath, so you can simplify this to the following:
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
Like this:
const List = Immutable.List;
const items = [{
id: 1,
subList: []
},
{
id: 2,
subList: []
},
{
id: 3,
subList: []
}
];
const newItem = {
name: "sublist item"
};
let collection = Immutable.fromJS(items);
collection = collection.updateIn([0, 'subList'], subList => {
return subList.concat(newItem);
});
console.log(collection)
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.2/immutable.js"></script>

Use the object you got, update the subArray and return the whole object.
const List = Immutable.List;
const items = [
{ id: 1, subList: [] },
{ id: 2, subList: [] },
{id: 3, subList: [] }
];
const newItem = { name: "sublist item" };
let collection = List(items);
collection = collection.update([0], function (obj) {
obj.subList = obj.subList.concat(newItem)
return obj;
});

This doesn’t work because the elements of your Immutable.List are plain-old JavaScript objects (POJO), not Immutable.Maps, so updateIn doesn’t know how to work with them. You can either:
Make the objects Immutable.Maps by using Immutable.fromJS instead of Immutable.List as the constructor to convert the entire object graph to Immutable objects. (See JS Bin)
Use update([0]) instead of updateIn to just get the POJO and mutate it (as in #Navjot’s answer).

Related

How to get the values of an array of an array of objects

So I have an array like this. Like array containing an array of objects.
posts = [
[
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
}
],
[
{
"id": 2,
"info": "This is a duplicated id I want to remove"
},
{
"id": 4,
"info": "This information is safe"
}
]
]
I want to get the elements from each array and create a new array that only has the objects at the same time removing duplicated ids.
What am trying to achieve is something like
posts = [
{
"id": 2,
"info": "This is some information"
},
{
"id": 3,
"info": "This is the other information"
},
{
"id": 4,
"info": "This information is safe"
}
]
This is the code I have so far
id = ids.map(val => {
for(let i in val) {
console.log(val)
}
return something
})
I keep getting undefined values. I have tried forEach, for loop.
Any help would be much appreciated. Thanks
Use flat to get a flattened array of objects, and then loop over the array. If the current object's id can't be found in an object in the output array push that object.
const posts=[[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const out = [];
for (const obj of posts.flat()) {
const found = out.find(f => obj.id === f.id);
if (!found) out.push(obj);
}
console.log(out);
You could use .flat() and then .filter():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = posts.flat().filter((x, i, self) => i === self.findIndex((y) => x.id === y.id));
console.log(newPosts);
Another potential (and more optimal) solution could be this using .reduce():
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const newPosts = Object.values(posts.flat().reduce((acc, curr) => {
return {
...acc,
...(!acc[curr.id] ? { [curr.id]: curr } : undefined),
};
}, {}));
console.log(newPosts);
Or, if you don't like .reduce(), you can do something very similar with the Map object and a for...of loop:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const map = new Map();
for (const item of posts.flat()) {
if (map.has(item.id)) continue;
map.set(item.id, item);
}
const newPosts = Array.from(map.values());
console.log(newPosts);
Or even use a classic for loop to get the job done:
const posts = [
[
{
id: 2,
info: 'This is some information',
},
{
id: 3,
info: 'This is the other information',
},
],
[
{
id: 2,
info: 'This is a duplicated id I want to remove',
},
{
id: 4,
info: 'This information is safe',
},
],
];
const flattened = posts.flat();
const map = {};
for (let i = 0; i < flattened.length; i++) {
if (map[flattened[i].id]) continue;
map[flattened[i].id] = flattened[i];
}
console.log(Object.values(map));
Either way, in each of these examples we're following the same workflow:
Flatten the array so that all items are on the same level.
Filter out the items with the duplicate IDs.
I group by id in order to remove duplicates.
var posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
var agg = {}
posts.forEach(function(arr) {
arr.forEach(function(item) {
agg[item.id] = agg[item.id] || item
})
})
console.log(Object.values(agg))
.as-console-wrapper {
max-height: 100% !important
}
Flatten the array with flat, then use a set to keep track of the ids we already have. The ternary inside the filter is logic to check if the id is already in the set, and if it is, we filter the item out. Otherwise, we add the id back to the set.
const posts = [[{id:2,info:"This is some information"},{id:3,info:"This is the other information"}],[{id:2,info:"This is a duplicated id I want to remove"},{id:4,info:"This information is safe"}]];
const flat = posts.flat();
const ids = new Set();
const filtered = flat.filter((item) => ids.has(item.id) ? false : ids.add(item.id));
console.log(filtered);
.as-console-wrapper {
max-height: 100% !important
}
There are two things we need to do:
Flatten the inner areas into one main array with array.prototype.flat()
Remove duplicates based on, I'm assuming, the order of their appearance in the data.
We can do this by reducing the flattened array to an object with a condition that doesn't add any present id's if they're found
Then we convert that object to an array using Object.values()
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let flattened = posts.flat()
console.log('Flattened: ', flattened)
let unique = flattened.reduce((acc, obj) => {
if (!acc[obj.id]) {
acc[obj.id] = obj
}
return acc
}, {})
console.log('Unique Objects: ', unique)
let result = Object.values(unique)
console.log('Final Array: ' ,result)
Doing it in one go and with a spread ... object merge:
let posts = [ [ { "id": 2, "info": "This is some information" }, { "id": 3, "info": "This is the other information" } ], [ { "id": 2, "info": "This is a duplicated id I want to remove" }, { "id": 4, "info": "This information is safe" } ] ]
let result = Object.values(
posts.flat().reduce((acc, obj) =>
({...{[obj.id]: obj}, ...acc})
, {})
);
console.log('Final Array: ', result);

Reference id into array and map it

i have such an array.
[
{
id: 1,
title: "one",
cats: [],
},
{
id: 2,
title: "two",
cats: [{ id: 3 }, { id: 4 }],
},
{
id: 3,
title: "sub 1",
cats: [],
},
{
id: 4,
title: "sub 2",
cats: [],
},
];
How can i correctly reference the id 3 and 4 to the nested array of cats.
I need to achive the following.
I need to display the list as buttons, but the ones that have nested to be dropdown.
example
one.title
two.title
sub1.title
sub2.title
I dont want to have id 3 and 4 data like title, into the nested array because the router takes ID as param, so basically when i will click on sub1 it should display data from id-3.
Please help me understand this as i am new.
Thank you.
You can use this function to fomat your array:
const formatArray = arr =>
arr.map(item => {
if (item.cats.length === 0) {
return item;
} else {
const itemtFormatted = { ...item };
itemtFormatted.cats = item.cats.map(cat =>
arr.find(e => e.id === cat.id)
);
return itemtFormatted;
}
});
You can check here: https://jsfiddle.net/x9o6dkum/

How to add label to js array?

Hi I am looking to create an array that looks similar to this
const userList = {
123: "Tom",
124: "Michael",
125: "Christin",
};
it contains both value and label, what I tried so far
let raw = []
for (let x in data) {
raw.push(data[x].facility_name : data[x].id)
}
but it didn't work because "," was expected, if someone can help please
You are confusing arrays and objects. You need to add a key to an object not push. I kept it as a for in loop, but a for of loop would make more sense.
const data = [
{ id: 1, facility_name: "foo1" },
{ id: 2, facility_name: "foo2" },
{ id: 3, facility_name: "foo3" }
];
let raw = {};
for (let x in data) {
raw[data[x].id] = data[x].facility_name;
}
console.log(raw);
How I would code it using reduce.
var data = [
{ id: 1, facility_name: "foo1" },
{ id: 2, facility_name: "foo2" },
{ id: 3, facility_name: "foo3" }
];
const raw = data.reduce(function (acc, facility) {
acc[facility.id] = facility.facility_name;
return acc;
}, {})
console.log(raw);
IF your data has nested objects then you might do this:
let raw = {};
for(x in data)
{
raw[data[x].facility_name] = data[x].id;
}
This is useful when you want to get rid of duplicates.

Javascript - Get a corresponding object between two arrays

I have the following arrays:
First array:
const dummyJSON = [
{
id: 1,
sponsor_date: '2020-08-16T22:45:03.154Z'
},
{
id: 2,
sponsor_date: '2020-09-16T22:45:03.154Z'
},
{
id: 3,
sponsor_date: '2020-09-01T22:45:03.154Z'
}
]
Second array:
const validated = [ true, false, false ]
And I wanted to get the object (dummyJSON.id) when the corresponding (validated) array item is true.
Basically, if the first item in the validate [0] array has a value of "true", then I would like to have the corresponding [0] item's id value in the dummyJSON array.
You can use Array#reduce to get array of validated ids.
It will basically loop over every element and if the index of currently iterated object corresponds to the truthy value inside validated with the very same index, the object's id will be pushed to the result.
const dummyJSON = [
{ id: 1, sponsor_date: '2020-08-16T22:45:03.154Z' },
{ id: 2, sponsor_date: '2020-09-16T22:45:03.154Z' },
{ id: 3, sponsor_date: '2020-09-01T22:45:03.154Z' }
];
const validated = [true, false, false];
const validatedIds = dummyJSON
.reduce((s, { id }, i) => (validated[i] ? s.push(id) : s, s), []);
console.log(validatedIds);
If your goal is just to get the validated items, use filter:
const valid = dummyJSON.filter((item, index) => validated[index]);
If you just want the ids, add a map call:
const valid = dummyJSON.filter((item, index) => validated[index]);
const ids = valid.map(x => x.id);
This could be done in a single line if you prefer, by chaining the map call:
const ids = dummyJSON.filter((item, index) => validated[index]).map(x => x.id);
const dummyJSON = [
{ id: 1, sponsor_date: '2020-08-16T22:45:03.154Z' },
{ id: 2, sponsor_date: '2020-09-16T22:45:03.154Z' },
{ id: 3, sponsor_date: '2020-09-01T22:45:03.154Z' }
];
const validated = [ true, false, false ];
// source objects
console.log(dummyJSON.filter((_, index) => validated[index]));
// just the ids
console.log(dummyJSON.filter((_, index) => validated[index]).map(x => x.id));
No need for reduce, filter can do that just as well and faster :
const validated = [ true, false, false ]
const dummyJSON = [
{
id: 1,
sponsor_date: '2020-08-16T22:45:03.154Z'
},
{
id: 2,
sponsor_date: '2020-09-16T22:45:03.154Z'
},
{
id: 3,
sponsor_date: '2020-09-01T22:45:03.154Z'
}
]
// To get all validated objects from dummy JSON
const validatedJSON = dummyJSON.filter((obj, index) => validated[index])
// To extract just the ID's
const validatedJSONIds = validatedJSON.map(json => json.id)

Create new array from iterating JSON objects and getting only 1 of its inner array

See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/

Categories

Resources