Javascript loop over object and create missing objects from array - javascript

I have an object of data that is loaded from a database. A user can select options on the page and for each of those options that don't exist in the initial object, I need to create them.
I wrote up some psuedo code for what I am attempting here, its causing a memory issue though and crashing.
The goal here is that I iterate over each task > Roles array and check to see if theres any missing objects that need to be created. The object that need to be created come from selectedRoles.
Desired Outcome:
In the first task, it should create 3 roles as its currently empty. In the second task, it would create roles 1 and 3 because a role with the id of 2 already exists.
var tasks = [{
"User": {
"FirstName": "Joe",
"LastName": "Dirt",
"selected": false,
"marked": false
},
"Tool": {
"id": 31,
"text": "Admin",
"ToolID": "31",
"ToolName": "Admin",
"ToolSuite": "Enterprise Product"
},
"Roles": []
}, {
"User": {
"FirstName": "Bart",
"LastName": "Simpson",
"selected": false,
"marked": false
},
"Tool": {
"id": 35,
"text": "Wordpress",
"ToolID": "35",
"ToolName": "Wordpress",
"ToolSuite": "Enterprise Product"
},
"Roles": [{
RoleName: 'Role 2',
Role: 2,
RoleID: 2
}]
}];
// New selected roles from list
var selectedRoles = [1, 2, 3];
////////////////////////////////////////////////////////////////////////
/*
Loop over the configured tasks and
if there is not an existing role
matching a role id from "SelectedRoles",
create a new role within that task.
*/
// Loop over the tasks
tasks.forEach((task) => {
// If we have roles, loop over them
if (task.Roles.length) {
for (let i = 0; i < task.Roles.length; i++) {
// If this roleID does not exist in our selectedRoles, create the task
if(selectedRoles.indexOf(task.Roles[i].RoleID) >= 0){
// Create this role and add it to our task
task.Roles.push(createRole('Role ' + task.Roles[i].RoleID, task.Roles[i].RoleID, task.Roles[i].RoleID));
}
}
}
});
console.log(tasks)
function createRole(RoleName, RoleID, Role){
return {
RoleName: RoleName,
RoleID: RoleID,
Role: Role
}
}
Any thoughts on a more clean (and working) way to handle this?

var tasks = [{
"User": {
"FirstName": "Joe",
"LastName": "Dirt",
"selected": false,
"marked": false
},
"Tool": {
"id": 31,
"text": "Admin",
"ToolID": "31",
"ToolName": "Admin",
"ToolSuite": "Enterprise Product"
},
"Roles": []
}, {
"User": {
"FirstName": "Bart",
"LastName": "Simpson",
"selected": false,
"marked": false
},
"Tool": {
"id": 35,
"text": "Wordpress",
"ToolID": "35",
"ToolName": "Wordpress",
"ToolSuite": "Enterprise Product"
},
"Roles": [{
RoleName: 'Role 2',
Role: 2,
RoleID: 2
}]
}];
// New selected roles from list
var selectedRoles = [1, 2, 3];
////////////////////////////////////////////////////////////////////////
/*
Loop over the configured tasks and
if there is not an existing role
matching a role id from "SelectedRoles",
create a new role within that task.
*/
tasks.forEach((task) => {
if(task.Roles.length == 0){
for(var i = 0; i < selectedRoles.length; i++){
task.Roles.push(createRole('Role ' + selectedRoles[i], selectedRoles[i], selectedRoles[i]));
}
}
else{
var concatRoleArr = [];
var roleIdArr = [];
for(var i = 0; i < task.Roles.length; i++){
roleIdArr.push(task.Roles[i].RoleID);
}
for(var i = 0; i < selectedRoles.length; i++){
var roleIndex = roleIdArr.indexOf(selectedRoles[i]);
if(roleIndex < 0){
concatRoleArr.push(createRole('Role ' + selectedRoles[i], selectedRoles[i], selectedRoles[i]));
}
}
task.Roles = task.Roles.concat(concatRoleArr);
}
});
console.log(tasks);
function createRole(RoleName, RoleID, Role){
return {
RoleName: RoleName,
RoleID: RoleID,
Role: Role
}
}
Here is your updated code with what you are after. We changed up your looping. We start with checking if there are any roles at all. If not, we loop through selectedRoles and add each one to the role.
If there are roles existing in the task, we create a new array (concatRoleArr), gather all roleIds into a new array, Then loop through selectedRoles again. If the array of existing role ids does not contain the currently looked at selectedRole (indexOf is -1), then add it to the new concatRoleArr, otherwise, continue. When we are done, concat the task.Roles array with concatRoleArr.
You were running into memory issues because your original code has an infinite loop. You add roles to task.Roles, so the loop just keeps on going because no end of task.Roles is ever reached with your for loop when you keep adding more to it. And it kept adding more because checking indexOf with >= 0 will be true if it EXISTS within the array, which your role always did because you were just looping through existing roles anyway. Checking < 0 checks if it DOES NOT EXIST, since it will return -1.

Related

Conditionally combine arrays of objects based on Ids

How can I add the status property from object2 into object1 based on newEmployeeId matching employeeId only if the dependentId is NULL.
For example: An array where Ben, Jim and Dan have statuses of Complete, Updating and Finished, respectively. Lauren should not have a status.
var object1 = [
{ name: 'Ben', employeeId: 1, dependentId: null },
{ name: 'Lauren', employeeId: 1, dependentId: 5},
{ name: 'Jim', employeeId: 2, dependentId: null },
{ name: 'Dan', employeeId: 3, dependentId: null}
];
var object2 = [
{ status: 'Complete', newEmployeeId: 1 },
{ status: 'Updating', newEmployeeId: 2 },
{ status: 'Finished', newEmployeeId: 3 }
];
There are a number of ways to approach this. Here's one:
object1.forEach(o1 => {
if (o1.dependentId !== null) return;
const o2 = object2.find(o2 => o2.newEmployeeId === o1.employeeId);
if (!o2) return;
o1.status = o2.status;
})
The idea is that we loop over each entry of object1 via the forEach() array method. For each such entry o1, we first check to make sure that its dependentId is null (because you only want to operate on such entries) and give up if it isn't.
Then we search the object2 array for a matching entry o2 via the find() array method. If no such entry is found, o2 will be undefined and we we give up. Otherwise we set o1's status property to match that of o2.
For the example code you gave, this produces the following final state for object1:
console.log(object1);
/* [{
"name": "Ben",
"employeeId": 1,
"dependentId": null,
"status": "Complete"
}, {
"name": "Lauren",
"employeeId": 1,
"dependentId": 5
}, {
"name": "Jim",
"employeeId": 2,
"dependentId": null,
"status": "Updating"
}, {
"name": "Dan",
"employeeId": 3,
"dependentId": null,
"status": "Finished"
}] */
Note that, depending on your use cases, you might want to change the implementation. For example, if your arrays have many entries, you might want to index object2 by newEmployeeId ahead of time instead of finding its elements over and over again. But that's outside the scope of the question as asked.
Playground link to code
Try this:
object1.forEach(item => {
if (!item.dependentId) {
result = object2.find(item2 => item2.newEmployeeId === item.employeeId)
item.status = result.status
}
})

Concatenate arrays from the same array in JavaScript

I have an array like this:
[
{
"id": 10002,
"flag": false,
"list": [
"aaa",
"bbb"
]
},
{
"id": 10001,
"flag": true,
"list": [
"10002",
"10003"
]
},
{
"id": 10003,
"flag": false,
"list": [
"ccc",
"ddd"
]
}
]
i tried this
initially i have "10001" value so iterate this array to take "list" array if flag==true then stored into newarray. but its not working.
I want it to be like this: [ "aaa", "bbb", "ccc", "ddd" ].
If i understand correctly this is what you want:
const someArray = [
{
"id": 10001,
"list": [
"10002",
"10003"
]
},
{
"id": 10002,
"list": [
"aaa",
"bbb"
]
},
{
"id": 10003,
"list": [
"ccc",
"ddd"
]
}
];
const [head,...rest] = someArray;
const result = head.list.reduce((acc,currentId)=>acc.concat(rest.find(({id})=> id === parseInt(currentId)).list),[]);
Here is a jsFiddle https://jsfiddle.net/sudakatux/9hju85mt/22/
Explanation:
take the head and splitted from the rest since the head contains the ids.
using the head as a dictionary find each list for each id in the head and concatenate
note the id must be in the subsequent list else it will fail with undefined. if you want to account for this error you can set a defualt empty object with a list. for example this part:
rest.find(({id})=> id === parseInt(currentId)).list
Will look like
rest.find(({id})=> id === parseInt(currentId)) || {list:[]}).list
Which basically means if its undefined return an object that has an empty list so then it will concatenate an empty list which results in being the same list. (like multiplying by 1 in a multiplication)
Hope it helps.
EDIT after your edit.
If your array is in different order you need to find the dictonary and then the logic is the same
const [newHead] = otherArray.filter(({list}) => list.every(elem=>!isNaN(elem)));
const result2 = newHead.list.reduce(
(acc,currentId) =>acc.concat(otherArray.find(({id})=> id === parseInt(currentId)).list),[]);
if you are testing for the flag then your head filter would look like. the blocks are the same the only thing that changes is the condition.
const [newHead] = otherArray.filter(({flag}) => flag));
(note* that instead of using the rest i used the complete array(otherArray). since im targeting equality.
Im using filter and extracting the first element of the result. because im accounting for the possibility that in the future you may have more than one "dictionary element". if thats the case in the future then you just have to concat the lists from the filter result
const array = [
{
id: 10001,
flag: true,
list: ["10002", "10003"]
},
{
flag: false,
id: 10002,
list: ["aaa", "bbb"]
},
{
flag: false,
id: 10003,
list: ["ccc", "ddd"]
}
];
const isHead = item => item.flag && item.id === 10001;
const head = array.find(isHead);
const rest = array.filter(item => !isHead(item));
const result = rest
.flatMap(item =>
head.list.includes(item.id.toString()) && item.list
);
console.log(result);
You can map over the list of the first item and concat all the lists from those ids.
const mapItems = (input) => {
const source = input[0].list;
source.reduce((results, id) => {
return results.concat(input.find(item => item.id === id).list);
}, []);
};
mapItems([
{
"id": 10001,
"list": [
"10002",
"10003"
]
},
{
"id": 10002,
"list": [
"aaa",
"bbb"
]
},
{
"id": 10003,
"list": [
"ccc",
"ddd"
]
}
]);
You can fetch the values of the list of first object in the array as arr[0]['list']
Once you have these values (10002,10003) then you can fetch the list values of remaining objects in the array whose id key matches one of the above values.
if(arr[i]['id'] == 10002 || arr[i]['id'] == 10003){
//fetch the list values
}

unable to push and append data to the state using React js

here i state with data
state = {
Response: [
{
"id": "15071",
"name": "John",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15072",
"name": "maxr",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15073",
"name": "Josef",
"salary": "53",
"age": "23",
"department": "admin"
},
{
"id": "15074",
"name": "Ye",
"salary": "53",
"age": "23",
"department": "admin"
}
]
i am displaying these records in the table. In table u will see 10 records and there will be a button on top of table so if append button is pressed then 10 records has to be added on every button press and the data has to be same but it has to be appended using the below logic i am trying to set the state by pushing 10 records and trying to append it for ex if i have 1,2,3,4,5,6,7,8,9,10 if i press append 1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10 has to be apeended
appendEmployees() {
var reLoadCount = 1;
for (let i = 0; i < 10; i++) {
const myObj = {
id: 0,
name: '',
salary: 0,
department: ''
};
myObj.id = +this.setState.employee[i].id + (reLoadCount * 10);
myObj.name = this.setState.employee[i].name;
myObj.salary = this.setState.employee[i].salary;
myObj.department = this.setState.employee[i].department;
this.setState.employee.push(myObj);
}
reLoadCount++;
}
am i doing some thing wrong here
If I get this right you're trying to add 10 duplicates of the objects in the this.state.employee array, the only difference between these new objects and the existing ones is their id.
If that is the case, here is how you can do that:
appendEmployees() {
this.setState(prevState => {
// Get the biggest ID number.
const maxId = Math.max(...prevState.employee.map(e => parseInt(e.id)));
// create 10 new employees copies of the first 10.
const newEmployees = prevState.employee.slice(0, 10).map((e, i) => ({
...e,
id: (maxId + i + 1)
}));
// return/update the state with a new array for "employee" that is a concatenation of the old array and the array of the 10 new ones.
return {
employee: [...prevState.employee, ...newEmployees]
}
});
}
I've added some comments to the example to explain what it does.
The important thing is this.setState which is the function used to update the state, here, I've used it with a function as the first parameter (it works with objects as well), I've did that because it is the preferred way of generating a new state that is derived from the old state.

Knockout.js updating array within an array

I have an array in Knockout view model which looks like so:
this.Activities = ko.observableArray([
{ "date": "28/11/2012 00:00:00",
"activities": [
{ "company": "BOW",
"description": "Backup Checks",
"length": "60"
},
{ "company": "AMS",
"description": "Data Request",
"length": "135"
},
]},
{ "date": "30/11/2012 00:00:00",
"activities": [
{ "company": "BOW",
"description": "Backup Checks",
"length": "60"
},
{ "company": "SLGT",
"description": "Software Development",
"length": "240"
},
{ "company": "BOW",
"description": "Data Request",
"length": "30"
},
]},
]);
I use this code to add a new element to it:
this.Activities.push(new NewActivity(company, description, length, fullDate));
Which uses NewActivity function:
function NewActivity(company, description, length, date) {
this.date = date;
this.activities = [{ "company": company, "description": description, "length": length }];
}
And it works fine. However, it creates an entirely new object every time it is getting released. I need to implement a condition when the code would check for the date of the objects already created. If the newly created object had the same date, activity details should be added to activities array within the Activities array for that date.
How can I do it?
All of the data for the Activities array comes from the model in the strongly typed view in MVC application:
this.Activities = ko.observableArray([
#foreach (ActivitySet aSet in Model)
{
#:{ "date": "#aSet.Date",
#:"activities": [
foreach(Activity a in aSet.Activities)
{
#:{ "company": "#a.Companies.Select(c => c.Title).Single()",
#:"description": "#a.Descriptions.Select(c => c.Title).Single()",
#:"length": "#a.LengthInMinutes"
#:},
}
#:]},
}
]);
I suggest that you create a few entities describing your activities:
// Details
function ActivityDetails(company, description, length) {
this.company = ko.observable(company);
this.description = ko.observable(description);
this.length = ko.observable(length);
}
// Activity
function Activity(date, activityDetails) {
this.date = ko.observable(date);
this.details = ko.observableArray(activityDetails);
}
The you can control activities in the following manner:
function ViewModel () {
var self = this;
this.Activities = ko.observableArray([
// Activities here
]);
this.addActivity = function (activity) {
var flag = false;
ko.utils.arrayMap(self.Activities(), function (item) {
// Flag is here so it doesn't keep checking further in iterations
if (!flag) {
if (item.date() === activity.date()) {
item.details.push(activity.details);
flag = true;
}
}
});
// Case where activity date was not found in existing records
if (!flag) {
self.Activities.push(activity);
}
}
}
This requires your view model to have a custom add method which I have provided an example of. Note that everywhere I am resolving observable values, so if you are using non-observable ones remove the function calls.

Remove JSON element

I want to remove JSON element or one whole row from JSON.
I have following JSON string:
{
"result":[
{
"FirstName": "Test1",
"LastName": "User",
},
{
"FirstName": "user",
"LastName": "user",
},
{
"FirstName": "Ropbert",
"LastName": "Jones",
},
{
"FirstName": "hitesh",
"LastName": "prajapti",
}
]
}
var json = { ... };
var key = "foo";
delete json[key]; // Removes json.foo from the dictionary.
You can use splice to remove elements from an array.
Do NOT have trailing commas in your OBJECT (JSON is a string notation)
UPDATE: you need to use array.splice and not delete if you want to remove items from the array in the object. Alternatively filter the array for undefined after removing
var data = {
"result": [{
"FirstName": "Test1",
"LastName": "User"
}, {
"FirstName": "user",
"LastName": "user"
}]
}
console.log(data.result);
console.log("------------ deleting -------------");
delete data.result[1];
console.log(data.result); // note the "undefined" in the array.
data = {
"result": [{
"FirstName": "Test1",
"LastName": "User"
}, {
"FirstName": "user",
"LastName": "user"
}]
}
console.log(data.result);
console.log("------------ slicing -------------");
var deletedItem = data.result.splice(1,1);
console.log(data.result); // here no problem with undefined.
You can try to delete the JSON as follows:
var bleh = {first: '1', second: '2', third:'3'}
alert(bleh.first);
delete bleh.first;
alert(bleh.first);
Alternatively, you can also pass in the index to delete an attribute:
delete bleh[1];
However, to understand some of the repercussions of using deletes, have a look here
For those of you who came here looking for how to remove an object from an array based on object value:
let users = [{name: "Ben"},{name: "Tim"},{name: "Harry"}];
let usersWithoutTim = users.filter(user => user.name !== "Tim");
// The old fashioned way:
for (let [i, user] of users.entries()) {
if (user.name === "Tim") {
users.splice(i, 1); // Tim is now removed from "users"
}
}
Note: These functions will remove all users named Tim from the array.
I recommend splice method to remove an object from JSON objects array.
jQuery(json).each(function (index){
if(json[index].FirstName == "Test1"){
json.splice(index,1); // This will remove the object that first name equals to Test1
return false; // This will stop the execution of jQuery each loop.
}
});
I use this because when I use delete method, I get null object after I do JSON.stringify(json)
All the answers are great, and it will do what you ask it too, but I believe the best way to delete this, and the best way for the garbage collector (if you are running node.js) is like this:
var json = { <your_imported_json_here> };
var key = "somekey";
json[key] = null;
delete json[key];
This way the garbage collector for node.js will know that json['somekey'] is no longer required, and will delete it.
Fix the errors in the JSON: http://jsonlint.com/
Parse the JSON (since you have tagged the question with JavaScript, use json2.js)
Delete the property from the object you created
Stringify the object back to JSON.
As described by #mplungjan, I though it was right. Then right away I click the up rate button. But by following it, I finally got an error.
<script>
var data = {"result":[
{"FirstName":"Test1","LastName":"User","Email":"test#test.com","City":"ahmedabad","State":"sk","Country":"canada","Status":"False","iUserID":"23"},
{"FirstName":"user","LastName":"user","Email":"u#u.com","City":"ahmedabad","State":"Gujarat","Country":"India","Status":"True","iUserID":"41"},
{"FirstName":"Ropbert","LastName":"Jones","Email":"Robert#gmail.com","City":"NewYork","State":"gfg","Country":"fgdfgdfg","Status":"True","iUserID":"48"},
{"FirstName":"hitesh","LastName":"prajapti","Email":"h.prajapati#zzz.com","City":"","State":"","Country":"","Status":"True","iUserID":"78"}
]
}
alert(data.result)
delete data.result[3]
alert(data.result)
</script>
Delete is just remove the data, but the 'place' is still there as undefined.
I did this and it works like a charm :
data.result.splice(2,1);
meaning : delete 1 item at position 3 ( because array is counted form 0, then item at no 3 is counted as no 2 )
Try this following
var myJSONObject ={"ircEvent": "PRIVMSG", "method": "newURI", "regex": "^http://.*"};
console.log(myJSONObject);
console.log(myJSONObject.ircEvent);
delete myJSONObject.ircEvent
delete myJSONObject.regex
console.log(myJSONObject);
if we want to remove one attribute say "firstName" from the array
we can use map function along with delete as mentioned above
var result= [
{
"FirstName": "Test1",
"LastName": "User",
},
{
"FirstName": "user",
"LastName": "user",
},
{
"FirstName": "Ropbert",
"LastName": "Jones",
},
{
"FirstName": "hitesh",
"LastName": "prajapti",
}
]
result.map( el=>{
delete el["FirstName"]
})
console.log("OUT",result)
Could you possibly use filter? Say you wanted to remove all instances of Ropbert
let result = [
{
"FirstName": "Test1",
"LastName": "User",
},
{
"FirstName": "user",
"LastName": "user",
},
{
"FirstName": "Ropbert",
"LastName": "Jones",
},
{
"FirstName": "hitesh",
"LastName": "prajapti",
}
]
result = result.filter(val => val.FirstName !== "Ropbert")
(result now contains)
[
{
"FirstName": "Test1",
"LastName": "User",
},
{
"FirstName": "user",
"LastName": "user",
},
{
"FirstName": "hitesh",
"LastName": "prajapti",
}
]
You can add further values to narrow down the items you remove, for example if you want to remove on first and last name then you could do this:
result = result.filter(val => !(val.FirstName === "Ropbert" && val.LastName === "Jones"))
Although as noted, if you have 4 'Ropbert Jones' in your array, all 4 instances would be removed.
try this
json = $.grep(newcurrPayment.paymentTypeInsert, function (el, idx) { return el.FirstName == "Test1" }, true)
A descent Solution is here
My JSON array is
const [myData, setData] = useState([{username:'faisalamin', share:20}, {username:'john', share:80}])
I want to delete john on a button click in renderItem or in .map
const _deleteItem = (item) => {
const newData = myData.filter(value => { return value.username !== item.username });
setData(newData);
}

Categories