Javascript Lodash replace array with another [duplicate] - javascript

This question already has answers here:
How can I access and process nested objects, arrays, or JSON?
(31 answers)
Closed 10 months ago.
I'm trying to manipulate an array like this:
data = [
{
"id":"1",
"items":[
{
"title":"item 1"
},
{
"title":"item 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
I need, for example, to push another array:
[
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
inside data[0].items and obtain:
data = [
{
"id":"1",
"items":[
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
...how can I do this maintaining immutability, for example with Lodash?
Not understand anding how to change only a specific sub object in a data structure.
Somebody have suggestions?
Thanks

Presented below is one possible way to immutably add given array into a particular index "items" prop.
Code Snippet
const immutableAdd = (aIdx, addThis, orig) => {
const newData = structuredClone(orig);
newData[aIdx].items = addThis;
return newData;
};
const data = [{
"id": "1",
"items": [{
"title": "item 1"
},
{
"title": "item 2"
}
]
},
{
"id": "2",
"items": [{
"title": "item2 1"
},
{
"title": "item2 2"
}
]
}
];
const addThisArr = [{
"title": "item new 1"
},
{
"title": "item new 2"
}
];
console.log('immutableAdd result: ', immutableAdd(0, addThisArr, data));
console.log('original data: ', data);
.as-console-wrapper { max-height: 100% !important; top: 0 }
Explanation
Use structuredClone() to deep-clone existing data array
Navigate to the aIdx of the cloned-array
Assign the given array (to be added) into aIdx's items prop
NOTE
This solution does not use lodash as it is not mandatory (to use lodash) to perform immutable operations.

If you want to maintain the immutability of original data, just map the content of original data to the new data as you want, and wrap your logic into a pure function to improve readability.
const dataOriginal = [{
"id": "1",
"items": [{
"title": "item 1"
},
{
"title": "item 2"
}
]
},
{
"id": "2",
"items": [{
"title": "item2 1"
},
{
"title": "item2 2"
}
]
}
]
const dataNew = createDataWithSomethingNew(dataOriginal, [{
"title": "item new 1"
},
{
"title": "item new 2"
}
])
function createDataWithSomethingNew(data, props) {
return data.map(function changeItemsOfId1ToProps(value) {
if (value.id === '1') {
return {
id: value.id,
items: props
}
} else {
return value
}
})
}

lodash has a method _.update can modify object with the correct path in string provided.
Another method _.cloneDeep can copy you object deeply. So that change in the pre-copied object will not affect the cloned object.
Finally use a deep freeze function to call Object.freeze recursively on the cloned object to make it immutable.
var data = [
{
"id":"1",
"items":[
{
"title":"item 1"
},
{
"title":"item 2"
}
]
},
{
"id":"2",
"items":[
{
"title":"item2 1"
},
{
"title":"item2 2"
}
]
}
]
var clonedData = _.cloneDeep(data) // copy the full object to avoid modify the source data
// update the data of that path '[0].items' in clonedData
_.update(clonedData, '[0].items', function(n) {
return [
{
"title":"item new 1"
},
{
"title":"item new 2"
}
]
})
// provide object immutability
const deepFreeze = (obj1) => {
_.keys(obj1).forEach((property) => {
if (
typeof obj1[property] === "object" &&
!Object.isFrozen(obj1[property])
)
deepFreeze(obj1[property])
});
Object.freeze(obj1)
};
deepFreeze(clonedData)
data[2] = {id: 3} // data will be changed
data[1].items[2] = {title: "3"} // and also this one
clonedData[2] = {id: 3} // nothing will be changed
clonedData[1].items[2] = {title: "3"} // and also this one
console.log(`data:`, data);
console.log(`clonedData:`, clonedData);
Runkit Example

Related

Javascript Grouping by object property, when property is an array

I currently have an array of items that looks a bit like this: I want to group the items by category lookup, with the slight problem that category lookup is potentially an array, such that Parent Item 2 would be listed twice (once in My Cat) and once in something else) I tried using https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/groupBy but it doesn't seem to be able to handle this?
[
{
"tool_id": "4-19de-454673d9-9ef5-4545",
"somekey" : "Parent Item 2"
"categoryLookup": [
{
"category_name": "New Item",
}
]
},
{
"tool_id": "be7ea707-19de-43d9-9ef1-d4a3ff79f77a",
"somekey" : "Parent Item"
"categoryLookup": [
{
"category_name": "My Cat",
},
{
"category_name": "Something Else",
}
]
}
]
The final result would look something like:
[
{
New Item: [
{...contains 4-19de-454673d9-9ef5-4545 }
],
My Cat: [
{...contains be7ea707-19de-43d9-9ef1-d4a3ff79f77a}
],
Something Else: [
{... contain be7ea707-19de-43d9-9ef1-d4a3ff79f77a}
]
}
]
You can iterate over the original array and create the final one:
var data = [{
"tool_id": "4-19de-454673d9-9ef5-4545",
"somekey": "Parent Item 2",
"categoryLookup": [{
"category_name": "New Item",
}]
},
{
"tool_id": "be7ea707-19de-43d9-9ef1-d4a3ff79f77a",
"somekey": "Parent Item",
"categoryLookup": [{
"category_name": "My Cat",
},
{
"category_name": "Something Else",
}
]
}
];
function groupByCategory(data) {
const res = {};
data.forEach(item => {
item.categoryLookup.forEach(category => {
const name = category.category_name;
res[name] ??= [];
res[name].push({
item: item.tool_id //or all the properties you want
});
});
});
return res;
}
console.log( groupByCategory(data) );

How can I print a specific part of a JSON object with JavaScript?

I would like to know what to use to transform the document from JSON to an array and the print the part of the array the user wants. Also, how can I put it in an HTML document so the user can search any part of the array.
Below is the JSON.
{
"A": {
"1": {
"1\u00ba": [
"Semestre 1"
]
},
"2": {
"1\u00ba": [
"Semestre 1"
]
}
},
"B": [
],
"c": {
"2": {
"1\u00ba": [
"Semestre 1"
]
},
"3": {
"1\u00ba": [
"Semestre 1"
]
},
"44": {
"1\u00ba": [
"Semestre 1"
]
},
"G6": {
"1\u00ba": [
"Semestre 1"
]
},
"GP98": {
"1\u00ba": [
"Semestre 1"
]
},
"654": {
"1\u00ba": [
"Semestre 1"
]
},
"5556": {
"1\u00ba": [
"Semestre 1"
]
},
"7654": {
"1\u00ba": [
"Semestre 1"
]
}
}
}
Thanks for your help.
Depending on what you want your strings/collection of strings to look like you can do one of the below options.
JSON.Stringify will convert a JavaScript object or value to a JSON string.
printDataWithStringify = (x) => {
console.log(JSON.stringify(data[x]))
}
> {"1":{"1º":["Semestre 1"]},"2":{"1º":["Semestre 1"]}}
If you want to go even further, you can try the below code.
var printedStrings = []
checkNestedData = (x, y) => {
if (typeof(x[y]) === 'object' && !Array.isArray(x[y][property1])) {
printedStrings.push(y)
for (var property1 in x[y]) {
checkNestedData(x[y], property1)
}
} else {
printedStrings.push(x[y]);
}
}
printDataWithKeysAndValues = (x) => {
var part = data[x]
for (var property1 in part) {
checkNestedData(part, property1)
}
console.log(printedStrings)
}
> 1,1º,Semestre 1,2,1º,Semestre 1
The above code utilizes a for...in loop which is used to loop through JavaScript objects. part is the object you get when you retrieve information from data at the key of x (in this case, "A"). property1 represents the key for the current object (part) and is the iterator for the for...in loop as we loop through part.
To take it even further, checkNestedData checks to see if there is another object nested in the current object. If it's an object (that doesn't have a array for a child), you push y (remember this is the key (property1) from the original loop) into the intialized printedStrings array. Then perform another loop and you run checkNestedData recursively on the current iterative in the new loop.
This recursive callback will run until the last child is not a populated object.
Once we've looped through the entire object, storing the keys and values we've extracted from the object (including nested objects) we simply console.log the final array which contains all of our keys and value from that portion ("A") of data.
Depending on how you want the string to be formatted, you can append/remove things from the strings via interpolation or concatenation. But this solution should get you every key and value and store them as a string in an array.
var data = {
"A": {
"1": {
"1\u00ba": [
"Semestre 1"
]
},
"2": {
"1\u00ba": [
"Semestre 1"
]
}
},
"B": [
],
"c": {
"2": {
"1\u00ba": [
"Semestre 1"
]
},
"3": {
"1\u00ba": [
"Semestre 1"
]
},
"44": {
"1\u00ba": [
"Semestre 1"
]
},
"G6": {
"1\u00ba": [
"Semestre 1"
]
},
"GP98": {
"1\u00ba": [
"Semestre 1"
]
},
"654": {
"1\u00ba": [
"Semestre 1"
]
},
"5556": {
"1\u00ba": [
"Semestre 1"
]
},
"7654": {
"1\u00ba": [
"Semestre 1"
]
}
}
}
printDataWithStringify = (x) => {
console.log('STRINGIFY: ' + JSON.stringify(data[x]))
}
var printedStrings = []
checkNestedData = (x, y) => {
if (typeof(x[y]) === 'object' && !Array.isArray(x[y][property1])) {
printedStrings.push(y)
for (var property1 in x[y]) {
checkNestedData(x[y], property1)
}
} else {
printedStrings.push(x[y]);
}
}
printDataWithKeysAndValues = (x) => {
var part = data[x]
for (var property1 in part) {
checkNestedData(part, property1)
}
console.log('ALL KEYS AND VALUES: ' + printedStrings)
}
printDataWithStringify("A")
printDataWithKeysAndValues("A")

Sort JSON or get JSON in original order [duplicate]

This question already has answers here:
Does ES6 introduce a well-defined order of enumeration for object properties?
(3 answers)
Closed 3 years ago.
Since it is not possible to get a JSON file with the original order, I'd like to sort it in JS again. The file looks like this:
{
"index": 5,
"timestamp": 1570438008,
"data": {
"12": [
"Title 2",
"Description 2"
],
"10": [
"Title 1",
"Description 1"
]
}
}
If I access this JSON file now from JS, I get another order than the original:
$.ajax({
url: '../json/smiirl_data.json',
dataType: 'json',
success: function(response) {
console.log(response['data']);
}
});
console.log from JS shows:
{
"10": [
"Title 1",
"Description 1"
],
"12": [
"Title 2",
"Description 2"
]
}
Is it possible to sort (title descending) it? I need the orignal order (12, 10).
at first you need to serialize object to array after that you use sort function to sort it.
var array = [],
data = response['data'];
for (var array in data ) {
array.push([array, data[vehicle]]);
}
array.sort(function(a, b) {
return b[1] - a[1];
});
Because objects are not always guaranteed to maintain its order, and JSON does not support the Map class which guarantees the order, you should use an array instead, where there are two elements: the key and the value.
For example:
"data": [
[12, ["Title 2", "Description 2"],
[10, ["Title 1", "Description 1"]
]
The you can access it like:
for(let item of myJSON.data)
{
console.log('Key:', item[0]);
console.log('Value:', item[1]);
}
To guarantee order, re-structure your data to be a 2-dimensional array.
You can create an index map, and lookup function to make the data easier to query.
let data = {
"index": 5,
"timestamp": 1570438008,
"data": [
[ "12", [ "Title 2", "Description 2" ] ],
[ "10", [ "Title 1", "Description 1" ] ]
]
}
let indexMap = indexLookupMap(data.data); // Build an index
console.log(lookupEntry(data.data, indexMap, '10')); // Find entry with id '10'
data.data.forEach(entry => {
console.log(`key => ${entry[0]}, value => ${entry[1].join(', ')}`);
})
function lookupEntry(data, indexMap, id) {
return data[indexMap[id]][1];
}
function indexLookupMap(data) {
return data.reduce((dict, entry, idx) => Object.assign(dict, { [entry[0]]: idx }), {});
}
.as-console-wrapper { top: 0; max-height: 100% !important; }

how to rearrange recursive json into tree structure with javascript?

I would like to transform the following JSON into another structure.
The source JSON:
values = array with objects wich needs to filtered by action === 'commented'
comment = object with the comment, n Tasks and n Comments
Comments can have endless more Comments and Tasks
{
"values": [
{
"action": "COMMENTED",
"comment": {
"text": "comment text",
"comments": [
{
"text": "reply text",
"comments": [],
"tasks": []
}
],
"tasks": [
{
"text": "task text",
"state": "RESOLVED"
}
]
}
}
]
}
The Target JSON:
Array(s) with Objects
each comment or tasks is a "children" (recursive!)
[
{
"text": "comment text",
"children": [
{
"text": "reply text",
"type": "comment"
},
{
"text": "task text",
"state": "RESOLVED"
}
]
}
]
Ive started with:
data = data.values.filter((e)=>{
return e.action === 'COMMENTED';
}).map((e)=>{
// hmmm recursion needed, how to solve?
});
data = data.values.filter(e => e.action === 'COMMENTED')
.map(function recursion({comment}){
return {
text: comment.text,
children: [...comment.comments.map(recursion), ...comment.tasks];
};
});
I ended up with:
let data = response.data.values
.filter(e => e.action === 'COMMENTED')
.map(function e({comment, commentAnchor}) {
return {
commentAnchor,
text: comment.text,
children: [...comment.comments.map(function recursion(comment) {
if (typeof comment === 'undefined') {
return {};
}
let children = [];
if (comment.comments) {
children.push(...comment.comments.map(recursion));
}
if (comment.tasks) {
children.push(...comment.tasks);
}
let _return = {
...comment,
text: comment.text
};
_return.children = children;
return _return;
}), ...comment.tasks]
}
});

Manipulating javascript object with underscore

I have a Javascript object with a format like below
"items":
{
"Groups":[
{
"title":"group 1",
"SubGroups":[
{
"title":"sub1",
"id" : "1",
"items":[
{
"title":"Ajax request 1",
},
{
"title":"Ajax request 2",
}
]
},
{
"title":"sub2",
"id" : "2",
"items":[
{
"title":"Ajax request 3",
},
{
"title":"Ajax request 4",
}
]
}
]
}
]
There are n 'Groups', n 'subGroups' and n 'items'.
What I want to do firstly is get all the items from a particular group based on id. This is achieved using:
_.each(items.Groups, function(o) {
result = _.where(o.SubGroups, {
'id': '1'
});
});
which returns
"items":[{"title":"Ajax request 1",},{"title":"Ajax request 2",}]
Then I want to get the rest of the data, excluding the items and parent group I have just retrieved.
I tried this:
_.each(items.Groups, function(o) {
arr = _.without(o.SubGroups, _.findWhere(o.SubGroups, {id: '2'}));
});
But this only returns me the items like this:
{
"title":"sub2",
"id" : "2",
"items":[{"title":"Ajax request 3"},{"title":"Ajax request 4",}]
}
whereas what I need is this:
"items":
{
"Groups":[
{
"title":"group 1",
"SubGroups":[
{
"title":"sub2",
"id" : "2",
"items":[
{
"title":"Ajax request 3",
},
{
"title":"Ajax request 4",
}
]
}
]
}
]
Just try this:
_.each(items.Groups, function(o) {
arr = _.without(o, _.findWhere(o.SubGroups, {id: '2'}));
});
o should be enough => you want to get Groups and not SubGroups.
Following is a pure JS implementation:
JSFiddle.
var data = {
"Groups": [{
"title": "group 1",
"SubGroups": [{
"title": "sub1",
"id": "1",
"items": [{
"title": "Ajax request 1",
}, {
"title": "Ajax request 2",
}]
}, {
"title": "sub2",
"id": "2",
"items": [{
"title": "Ajax request 3",
}, {
"title": "Ajax request 4",
}]
}]
}]
}
var items = [];
var group = [];
data.Groups.forEach(function(o) {
var _tmp = JSON.parse(JSON.stringify(o));
_tmp.SubGroups = [];
o.SubGroups.forEach(function(s) {
if (s.id == "1") {
items.push(s.items);
} else {
_tmp.SubGroups.push(s);
group.push(_tmp)
}
});
});
function printObj(label, obj) {
document.write(label + "<pre>" + JSON.stringify(obj, 0, 4) + "</pre>")
}
printObj("group", group);
printObj("items", items);
Using underscore and using your logic to filter all subgroups:
//array to store subgroup with ID 1
var results = [];
var d = _.each(data.items.Groups, function(o) {
result = _.where(o.SubGroups, {
'id': '1'
});
//add to results array
results.push(result);
});
//make a clone of the earlier object so that you get the parent structure.
var data1 = _.clone(data);
//set the filtered results to the group
data1.items.Groups = results;
//your data as you want
console.log(data1)
Working code here

Categories

Resources