Javascript : Modify object and add new index to a property - javascript

My object is something like:
let items =
[
{
"creationTimeStamp": "2022-05-31T17:04:28.000Z",
"modifiedTimeStamp": "2022-05-31T17:04:28.000Z",
"locations": [
{
"id": "5ao",
"name": "Store1"
}
],
"typeId": "Lead"
}
]
I am trying to push the following object into the locations property:
{
"id": "9a0",
"name": "Store2"
}
I have tried doing
items1 = [];
for (var i = 0; i < items.length; i++) {
items1.id = "9a0";
items1.name = "Store2";
//9 is some static index value added
Object.assign({9 : items1}, items[i].locations);
}
If I console(Object.assign({9 : items1}, items[i].locations)); I can see 2 arrays inside it, but my items locations property is still the same.
My expectation is as below:
[
{
"creationTimeStamp": "2022-05-31T17:04:28.000Z",
"modifiedTimeStamp": "2022-05-31T17:04:28.000Z",
"locations": [
{
"id": "5ao",
"name": "Store1"
},
{
"id": "9a0",
"name": "Store2"
}
],
"typeId": "Lead"
}
]
I also tried to use items[i].locations.push(item1) but then got:
TypeError: Cannot add property 9, object is not extensible
I also tried to assign a new array to items[i].locations, but then got:
TypeError: Cannot assign to read only property 'locations' of object '#'
What can I do to get the desired result?

You seem to expect that the second argument given to Object.assign will be mutated. But it is the first argument that is mutated. That means your .locations is not mutated. Moreover, in comments you indicate that locations cannot be extended and that the property is read-only.
So that means you'll need a complete new object.
Some other remarks:
Don't initialise items1 as an array, since it is supposed to be a plain object.
Declare a variable with const, let or var and avoid implicit global declaration.
It is safer to declare the items1 object inside the loop, so you create a new object each time and don't mutate the same object. For your example code it makes no difference, but it can lead to unexpected behaviour.
As you don't need i for anything else than items[i], and you actually need a complete new structure, use .map instead.
So:
items = items.map(item => {
let obj = {
id: "9a0",
name: "Store2"
};
return {...item, locations: item.locations.concat(obj) };
});

I always think in terms of functions, and of immutability-by-default, so my approach might look like this, with addLocationToAll built atop a simpler addLocation. The code is fairly simple:
const addLocation = (newLoc) => ({locations, ...rest}) =>
({...rest, locations: locations .concat (newLoc)})
const addLocationToAll = (newLoc) => (items) =>
items .map (addLocation (newLoc))
const items = [{creationTimeStamp: "2022-05-31T17:04:28.000Z", modifiedTimeStamp: "2022-05-31T17:04:28.000Z", locations: [{id: "5ao", name: "Store1"}], typeId:"Lead"}]
const newLoc = {id: "9a0", name: "Store2"}
console .log (addLocationToAll (newLoc) (items))
.as-console-wrapper {max-height: 100% !important; top: 0}

items is an array so it must access the first position of the array, which would be the proposed object.
With this, from the proposed object you will extract thelocation attribute and since this is an array, you use the push function to insert the new object
items[0]
// ->
// {
// creationTimeStamp: '2022-05-31T17:04:28.000Z',
// modifiedTimeStamp: '2022-05-31T17:04:28.000Z',
// locations: [ { id: '5ao', name: 'Store1' } ],
// typeId: 'Lead'
// }
I try this:
items[0].locations.push({"id": "9a0", "name": "Store2" })
And now:
items[0]
//->
// {
// creationTimeStamp: '2022-05-31T17:04:28.000Z',
// modifiedTimeStamp: '2022-05-31T17:04:28.000Z',
// locations: [ { id: '5ao', name: 'Store1' }, { id: '9a0', name: 'Store2' }],
// typeId: 'Lead'
// }

Related

JavaScript Replacing Object in Array with ID Number

So I have a series of objects that are pulled from an API and inputted into an array, something like such:
array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 1, name: "Second", relationship: "Friend"}
]
The user is allowed to add and remove objects to the list freely (they will appear within a Vue.JS DataTable), and said user is allowed a maximum of 4 objects within the array (lets say 4 "friends")
How should I go about implementing a function that searches the existing array (say, if its populated from the API), and inputs the new object with the corresponding ID that is missing (so if the user deletes the object with the id 2, and adds another, it will search said array with objects, find the missing id 2 slot in the array, and input the object in its place)?
Previously I have gone about it via implement array.find() with conditionals to see if the array contains or does not contain the certain id value, however, it searches through each entry and can end up inserting the same object multiple times. Another method I haven't attempted yet would be having a separate map that contains ids, and then when a user removes an object, having it correspond with the map, and vice versa when adding.
Any suggestions? Thanks
Instead of an array, I'd keep an object in data. Have it keyed by id, like this:
let objects = {
0: { id: 0, name: 'name0', relationship: 'relationship0' },
1: { id: 1, name: 'name1', relationship: 'relationship1' },
}
Integer keys in modern JS will preserve insertion order, so you can think of this object as ordered. The API probably returns an array, so do this...
// in the method that fetches from the api
let arrayFromApi = [...];
this.objects = array.reduce((acc, obj) => {
acc[obj.id] = obj; // insertion order will be preserved
return acc;
}, {});
Your UI probably wants an array, so do this (refer to "array" in the markup):
computed: {
array() {
return Object.values(this.objects);
},
To create a new object, insert it in order, minding the available keys. Note this is a linear search, but with small numbers of objects this will be plenty fast
methods: {
// assumes maxId is const like 4 (or 40, but maybe not 400)
createObject(name, relationship) {
let object = { name, relationship };
for (let i=0; i< maxId; i++) {
if (!this.objects[i]) {
object.id = i;
this.objects[i] = object;
break;
}
}
try this,
let array = [
{id: 0, name: "First", relationship: "Friend"},
{id: 4, name: "Second", relationship: "Friend"},
{id: 2, name: "Second", relationship: "Friend"},
]
const addItem = (item) => {
let prevId = -1
// this is unnecessary if your array is already sorted by id.
// in this example array ids are not sorted. e.g. 0, 4, 2
array.sort((a, b) => a.id - b.id)
//
array.forEach(ob => {
if(ob.id === prevId + 1) prevId++
else return;
})
item = {...item, id: prevId + 1 }
array.splice(prevId+1, 0, item)
}
addItem({name: "x", relationship: "y"})
addItem({name: "a", relationship: "b"})
addItem({name: "c", relationship: "d"})
console.log(array)
You can simply achieve this with the help of Array.find() method along with the Array.indexOf() and Array.splice().
Live Demo :
// Input array of objects (coming from API) and suppose user deleted 2nd id object from the array.
const arr = [
{id: 0, name: "First", relationship: "Friend" },
{id: 1, name: "Second", relationship: "Friend" },
{id: 3, name: "Fourth", relationship: "Friend" }
];
// find the objects next to missing object.
const res = arr.find((obj, index) => obj.id !== index);
// find the index where we have to input the new object.
const index = arr.indexOf(res);
// New object user want to insert
const newObj = {
id: index,
name: "Third",
relationship: "Friend"
}
// Insert the new object into an array at the missing position.
arr.splice(index, 0, newObj);
// Output
console.log(arr);

javascript replace property name in n-ary tree object

Supose I have a n-ary tree structure (in json) like this:
[
{
"text": "Some title",
"children": [
{
"text": "Some title",
"children": [
...
]
},
...
]
}
]
Where I neither know how many children the nodes will have nor the tree's depth.
What I would like to do is change the name of property text to name, across all children.
I've tryed this, with a recursive function func:
func(tree) {
if (!tree) return;
for (let node of tree) {
node.name = node.text
delete node.text;
return func(node.children);
}
}
But it didn't work. How would I do that?
I would say, the main problem with your code is that node variable holds the value of corresponding array items and it doesn't keep the reference to those items themselves, so, basically, mutations you attempt to make are never applied to original array (but only to temporary variable reassigned upon each loop iteration)
If you prefer to mutate original array and feel comfortable using for(-loops for that purpose, you'd be much better off using for(..in-loop to access array items by their keys:
const src = [
{
text: "Some title",
children: [
{
text: "Some title",
children: []
},
]
}
],
func = tree => {
for(const nodeIdx in tree){
const {text:name, children} = tree[nodeIdx]
func(children)
tree[nodeIdx] = {name, children}
}
}
func(src)
console.log(src)
.as-console-wrapper{min-height:100%;}
However, I would avoid mutating source data and return new array instead (e.g. with Array.prototype.map():
const src = [
{
text: "Some title",
children: [
{
text: "Some title",
children: []
},
]
}
],
func = tree =>
tree.map(({text:name,children}) => ({
name,
...(children && {children: func(children)})
}))
console.log(func(src))
.as-console-wrapper{min-height:100%;}
You would use the in operator here.
for (let node **in** tree) {
node.name = node.text
delete node.text;
return func(node.children);
}

Getting value through DOM and then filter an array for the value returns undefined

I have written a function which is supposed to filter through an array and return a value associated with the given value. For example, by knowing a name, I want to get the initials.
array
let members = [
{
id: 1,
memberFullName: "Ben Stiller",
memberInitials: "BS"
},
{
id: 2,
memberFullName: "Michael Jackson",
memberInitials: "MJ"
}
];
function
function findMemberInitials(object) {
let result = members.filter( obj => {
return obj.memberFullName == object;
})[0].memberInitials;
}
When I run the array like findMemberInitials("Ben Stiller"); it returns BS as expected.
When I try to get the value from at user input first and then run it through the function, it returns undefined.
let getMemberName = document.getElementById("name").value;
let getMemberInitials = findMemberInitials(getMemberName);
console.log(getMemberInitials); //returns undefined.
I'm not quite sure what I am doing wrong. I've tried parsing it to a string, but it didn't help.
You are not returning anything from function. Secondly you should use find()
let members = [
{
id: 1,
memberFullName: "Ben Stiller",
memberInitials: "BS"
},
{
id: 2,
memberFullName: "Michael Jackson",
memberInitials: "MJ"
}
];
function findMemberInitials(object) {
return (members.find(obj => obj.memberFullName === object) || {}).memberInitials;
}
console.log(findMemberInitials("Michael Jackson"))

Merge objects with corresponding key values from two different arrays of objects

I've got two arrays that have multiple objects
[
{
"name":"paul",
"employee_id":"8"
}
]
[
{
"years_at_school": 6,
"department":"Mathematics",
"e_id":"8"
}
]
How can I achieve the following with either ES6 or Lodash?
[
{
"name":"paul",
"employee_id":"8"
"data": {
"years_at_school": 6
"department":"Mathematics",
"e_id":"8"
}
}
]
I can merge but I'm not sure how to create a new child object and merge that in.
Code I've tried:
school_data = _.map(array1, function(obj) {
return _.merge(obj, _.find(array2, {employee_id: obj.e_id}))
})
This merges to a top level array like so (which is not what I want):
{
"name":"paul",
"employee_id":"8"
"years_at_school": 6
"department":"Mathematics",
"e_id":"8"
}
The connector between these two is "employee_id" and "e_id".
It's imperative that it's taken into account that they could be 1000 objects in each array, and that the only way to match these objects up is by "employee_id" and "e_id".
In order to match up employee_id and e_id you should iterate through the first array and create an object keyed to employee_id. Then you can iterate though the second array and add the data to the particular id in question. Here's an example with an extra item added to each array:
let arr1 = [
{
"name":"mark",
"employee_id":"6"
},
{
"name":"paul",
"employee_id":"8"
}
]
let arr2 = [
{
"years_at_school": 6,
"department":"Mathematics",
"e_id":"8"
},
{
"years_at_school": 12,
"department":"Arr",
"e_id":"6"
}
]
// empObj will be keyed to item.employee_id
let empObj = arr1.reduce((obj, item) => {
obj[item.employee_id] = item
return obj
}, {})
// now lookup up id and add data for each object in arr2
arr2.forEach(item=>
empObj[item.e_id].data = item
)
// The values of the object will be an array of your data
let merged = Object.values(empObj)
console.log(merged)
If you perform two nested O(n) loops (map+find), you'll end up with O(n^2) performance. A typical alternative is to create intermediate indexed structures so the whole thing is O(n). A functional approach with lodash:
const _ = require('lodash');
const dataByEmployeeId = _(array2).keyBy('e_id');
const result = array1.map(o => ({...o, data: dataByEmployeeId.get(o.employee_id)}));
Hope this help you:
var mainData = [{
name: "paul",
employee_id: "8"
}];
var secondaryData = [{
years_at_school: 6,
department: "Mathematics",
e_id: "8"
}];
var finalData = mainData.map(function(person, index) {
person.data = secondaryData[index];
return person;
});
Sorry, I've also fixed a missing coma in the second object and changed some other stuff.
With latest Ecmascript versions:
const mainData = [{
name: "paul",
employee_id: "8"
}];
const secondaryData = [{
years_at_school: 6,
department: "Mathematics",
e_id: "8"
}];
// Be careful with spread operator over objects.. it lacks of browser support yet! ..but works fine on latest Chrome version for example (69.0)
const finalData = mainData.map((person, index) => ({ ...person, data: secondaryData[index] }));
Your question suggests that both arrays will always have the same size. It also suggests that you want to put the contents of array2 within the field data of the elements with the same index in array1. If those assumptions are correct, then:
// Array that will receive the extra data
const teachers = [
{ name: "Paul", employee_id: 8 },
{ name: "Mariah", employee_id: 10 }
];
// Array with the additional data
const extraData = [
{ years_at_school: 6, department: "Mathematics", e_id: 8 },
{ years_at_school: 8, department: "Biology", e_id: 10 },
];
// Array.map will iterate through all indices, and gives both the
const merged = teachers.map((teacher, index) => Object.assign({ data: extraData[index] }, teacher));
However, if you want the data to be added to the employee with an "id" matching in both arrays, you need to do the following:
// Create a function to obtain the employee from an ID
const findEmployee = id => extraData.filter(entry => entry.e_id == id);
merged = teachers.map(teacher => {
const employeeData = findEmployee(teacher.employee_id);
if (employeeData.length === 0) {
// Employee not found
throw new Error("Data inconsistency");
}
if (employeeData.length > 1) {
// More than one employee found
throw new Error("Data inconsistency");
}
return Object.assign({ data: employeeData[0] }, teacher);
});
A slightly different approach just using vanilla js map with a loop to match the employee ids and add the data from the second array to the matching object from the first array. My guess is that the answer from #MarkMeyer is probably faster.
const arr1 = [{ "name": "paul", "employee_id": "8" }];
const arr2 = [{ "years_at_school": 6, "department": "Mathematics", "e_id": "8" }];
const results = arr1.map((obj1) => {
for (const obj2 of arr2) {
if (obj2.e_id === obj1.employee_id) {
obj1.data = obj2;
break;
}
}
return obj1;
});
console.log(results);

Iterating about values of an object array property

I use the Angular InMemoryService to store some fake data. I would like to transform the "HashtagDB" property into the array "hashtags". This "hashtags" array should only contain the values, but not the labels, so that I can use it to display it as Angular-Material-Chips.
As far as I understand, HashtagDB is an array property of an unnamed object. Is that correct? How do I transform the data from the array?
export class InMemoryDataService implements InMemoryDbService {
createDb() {
const person = [
{ Id: 1,
HashtagsDB: [ {hashtag: 'world'}, {hashtag: 'digital'}, {hashtag: 'economy'},
];
return {person};
}
}
hashtags: string[] = [/*'world', 'digital', 'economy*/];
You can project the HashtagsDB to a string[] by using map operator.
i.e.
var people = [
{
ID: 1,
HashtagsDB: [{hashtag: 'world'}, {hashtag: 'digital'}, {hashtag: 'economy'}]
}
];
// Hashtags of the first person.
var hashtags = people[0]['HashtagsDB'].map(item => item['hashtag']);
console.log(hashtags);
// Should returns
/*[
"world",
"digital",
"economy"
]*/

Categories