Swap rows/indexes within a JavaScript array using AngularJS - javascript

I have a custom directive that is holding an array of JavaScript objects.
The object is a little complex and lengthy but I will display something similar to point out my problem:
A JSON.stringify of this displays the following:
[
{
"Id": 1,
"Name": "John Doe",
"EMail": "john#doe.com"
},
{
"Id": 2,
"Name": "Jim Doe",
"EMail": "jim#doe.com"
},
{
"Id": 3,
"Name": "Jeff Doe",
"EMail": "jeff#doe.com"
}
]
I am further using ng-repeat to display the values in a tabular form on my HTML.
The values are coming from an API call that fetches them from a database.
I want to swap - say the entire Object with Id 1 with the entire Object with Id 3 so that during my tabular display I can see Id 3 object details first and Id 1 object details last, without breaking any functionality.
What would be the best possible solution to do this within the frontend itself?

How about just swapping them using a temp variable?
var arr = [{"Id":1,"Name":"John Doe","EMail":"john#doe.com"},
{"Id":2,"Name":"Jim Doe","EMail":"jim#doe.com"},
{"Id":3,"Name":"Jeff Doe","EMail":"jeff#doe.com"}]
var tmpObj = arr[0];
arr[0] = arr[2];
arr[2] = tmpObj;

If you want to reverse the array, use Array.prototype.reverse()
var app = angular.module("myApp", []);
app.controller("myController", function($scope) {
var arr = [
{
"Id": 1,
"Name": "John Doe",
"EMail": "john#doe.com"
},
{
"Id": 2,
"Name": "Jim Doe",
"EMail": "jim#doe.com"
},
{
"Id": 3,
"Name": "Jeff Doe",
"EMail": "jeff#doe.com"
}
];
$scope.array = arr.reverse();
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.0/angular.min.js"></script>
<div ng-app="myApp">
<div ng-controller="myController">
<div ng-repeat="item in array">
{{item.Id}} - {{item.Name}} - {{item.EMail}}
</div>
</div>
</div>

Related

Update properties of an object of an array

I have an array of objects and I need to update a few properties of a specific object inside the array and then run the findAndUpdateById call on it. I am trying to do this but its not updating and gives me an error of undefined name property. I guess I am not following the right procedure to update an update an object of javascript and because of this i need help.
Here is my array
let arr = [
{
"_id": "1234",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "johndoe#gmail.com",
"address": "123 Caroline Street"
}
}
]
Now I am recieving parameter like this that i need to update in my object
let ctx = {
"params": {
"changeObject": {
"firstName": "Ali",
"email": "ali#gmail.com"
}
}
}
Given: "I am getting the array of object by making .find call against id and there will be only one customer object in the received array"
// Given
let arr=[{"_id":"1234","customer":{"firstName":"John","lastName":"Doe","email":"johndoe#gmail.com","address":"123 Caroline Street"}}];
let ctx={"params":{"changeObject":{"lastName":"Ali"}}};
arr[0].customer = Object.assign(arr[0].customer, ctx.params.changeObject);
console.log(arr);
I think you were using different properties changePayload and changeObject.
You may want to use the spread operator and map for this as well.
let arr = [
{
"_id": "1234",
"customer": {
"firstName": "John",
"lastName": "Doe",
"email": "johndoe#gmail.com",
"address": "123 Caroline Street"
}
}
];
let ctx = {
"params": {
"changePayload": {
"firstName": "Ali",
"email": "ali#gmail.com"
}
}
}
arr = arr.map( data => ({
...data,
customer: {...data.customer, ...ctx.params.changePayload}
}) );
console.log( arr );
NOTE: This will change all objects in your array, but that is what you were asking for.

Typescript Filter and return single key

I have a set of checkboxes that allow a user to check which roles to assign to a new user. I am able to filter back and get only the checkboxes that are actually checked, however, I am having trouble finding the best way to just return the "name" key of those checked checkboxes.
userToAdd.roles = this.roles.filter( (role) => role.checked );
Is there a way to use a reduce, or basically just say "role.name" in the filter so I don't return the entire object? I can do this with a for loop, but I'm curious if there is a better way to just return the name key as part of the filter?
This is how the object looks now, which is wrong:
{
"firstName": "sfsdfds",
"username": "fdsfsdf",
"lastName": "sdfsdfsdf",
"email": "dsfsdfdsf",
"roles": [
{
"ID": "ce97fb46-7e04-4a4f-b393-5a5492b558fb",
"name": "admin",
"checked": true
},
{
"ID": "e89bacd2-4140-46a1-9a2b-0f85aa9f9ca0",
"name": "offline_access",
"checked": true
}
],
"password": "pass"
}
This is how the object should look, in the roles array i just include the name, not the ID or checked keys:
{
"firstName": "testing",
"lastName": "testing",
"username": "testing",
"email": "testing",
"roles": [
"uma_authorization",
"offline_access"
],
"password": "pass"
}
you could map after filtering. i.e:
userToAdd.roles = this.roles.filter( (role) => role.checked ).map(role => role.name;
You can achieve this using array map() method and object destructuring like:
userToAdd.roles = this.roles.filter(({checked}) => checked).map(({name}) => name);
The map() method creates a new array populated with the results of calling a provided function on every element in the calling array.
Yes you can use reduce.
const data = {
"firstName": "sfsdfds",
"username": "fdsfsdf",
"lastName": "sdfsdfsdf",
"email": "dsfsdfdsf",
"roles": [
{
"ID": "ce97fb46-7e04-4a4f-b393-5a5492b558fb",
"name": "admin",
"checked": true
},
{
"ID": "e89bacd2-4140-46a1-9a2b-0f85aa9f9ca0",
"name": "offline_access",
"checked": true
},
{
"ID": "e89bacd2-4140-46a1-9a2b-0f85aa9f9ca0",
"name": "offline_access2",
"checked": false
}
],
"password": "pass"
}
let filtered = data.roles.reduce((acc, curr)=>{
if(curr.checked) {
acc.push({
name: curr.name
})
}
return acc;
}, []);
console.log(filtered);
.filter().map() would also works but with reduce you don't have to iterate over array twice.
If you have linq, this is another option:
userToAdd.roles = from(this.roles).where(role => role.checked ).select(role =>role.name).toArray();
You can also use this:
this.roles.filter( (role) => role.checked )[0].anyPropert;

Link two objects in angular

I've got 2 data sets
posts
[
{
"postId": 1,
"postContent": "Lorem Ipsum",
"user": 1
},
{
"postId": 2,
"postContent": "Lorem Ipsum",
"user": 2
},
]
and users
[
{
"user": 1,
"firstName": "John",
"lastName": "Doe"
},
{
"user": 2,
"firstName": "Johny",
"lastName": "Doey"
}
]
I am repeating all the posts and i would like to access a user data throught the $scope.post.user.firstName etc.
how should i aproach it?
You can implement a method like getUserById() with the use of Array.prototype.find() to use it when iterating over your posts.
Code in AngularJS:
angular
.module('App', [])
.controller('AppController', ['$scope', function ($scope) {
$scope.posts = [{"postId": 1,"postContent": "Lorem Ipsum","user": 1},{"postId": 2,"postContent": "Lorem Ipsum","user": 2}];
$scope.users = [{"user": 1, "firstName": "John", "lastName": "Doe"},{ "user": 2, "firstName": "Johny", "lastName": "Doey"}];
$scope.getUserById = function (id) {
return $scope.users.find(function (user) {
return id === user.user;
});
};
}]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<div ng-app="App" ng-controller="AppController" class="row">
<div ng-repeat="p in posts">
<p>{{p.postContent}} <i>by {{getUserById(p.user).firstName}}</i></p>
</div>
</div>
find() the matching user for each post.
Note that if you are not using ES6 features, you will need a polyfill for Object.assign() and should use regular function() {}s instead array functions.
var posts = [{"postId":1,"postContent":"LoremIpsum","user":1},{"postId":2,"postContent":"LoremIpsum","user":2}];
var users = [{"user":1,"firstName":"John","lastName":"Doe"},{"user":2,"firstName":"Johny","lastName":"Doey"}];
var out = posts.map(post => Object.assign({}, post, {
user: users.find(user => user.user === post.user)
}));
console.log(out);
You can create a function that maps each user object to your post object. Then you can access user details as $scope.post.user.first_name; Here is simple code that will make you an new object with users.
var new_object = [];
function mapUsersToPosts(users, posts){
angular.forEach(posts, function(post){
angular.forEach(users, function(user){
if(post.user_id == user.user_id){
new_object.push({
'post' : post,
'user' : user
});
}
});
});
}
Now you can loop new_object and use user data and post data in same object.

compare 2 array of objects. match by ids, push object property into array

I have 2 arrays. users and posts. posts contain a property "post_by" which is the id of one of the users. I need to match the user and push the first & last name into the post object as a new property. Goal is I need to display the name of the user that made the post in a table.
note* I can use javascript, jquery, linq.js or lodash.
fiddle with json
fiddle
var users = [
{
"id": "15e640c1-a481-4997-96a7-be2d7b3fcabb",
"first_name": "Kul",
"last_name": "Srivastva",
},
{
"id": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd",
"first_name": "Rudy",
"last_name": "Sanchez",
},
{
"id": "636f9c2a-9e19-44e2-be88-9dc71d705322",
"first_name": "Todd",
"last_name": "Brothers"
},
{
"id": "79823c6d-de52-4464-aa7e-a15949fb25fb",
"first_name": "Mike",
"last_name": "Piehota"
},
{
"id": "e2ecd88e-c616-499c-8087-f7315c9bf470",
"first_name": "Nick",
"last_name": "Broadhurst"
}
]
var posts = [
{
"id": 1,
"status": "Active",
"post_title": "test title",
"post_body": "test body",
"post_by": "4cada7f0-b961-422d-8cfe-4e96c1fc11dd"
},
{
"id": 2,
"status": "Fixed",
"post_title": "test title two",
"post_body": "test body two",
"post_by": "79823c6d-de52-4464-aa7e-a15949fb25fb"
}
]
https://jsfiddle.net/zy5oe25n/7/
console.log($.map(posts, function(post){
var user = $.grep(users, function(user){
return user.id === post.post_by;
})[0];
post.first_name = user.first_name;
post.last_name = user.last_name;
return post;
}));
Here's a lodash approach:
_.map(posts, function(item) {
return _.assign(
_.pick(_.find(users, { id: item.post_by }),
'first_name', 'last_name'),
item
);
});
It's using map() to map the posts array to a new array of new objects (immutable data). It's then using find() to locate the user object, and uses pick() to get the properties we need. Finally, assign() adds the post properties to the new object that pick() created.
For good measure, using linq.js.
var userMap = Enumerable.From(users).ToObject("$.id");
posts.forEach(function (post) {
var user = userMap[post.post_by];
if (user) {
post.first_name = user.first_name;
post.last_name = user.last_name;
}
});
Note, we're using the builtin forEach() for arrays, linq.js is not needed for that part.

Mapping and binding nested objects and arrays

I have an object and within this object I have items and one of the items is an array which also contains objects. A sample of the data is shown below.
I am using knockout to bind this data to the view so I think I need to implement a double loop for returning the objects and the objects within the child array to be able to bind them in the view.
Sample data:
"singers": {
"ijiyt6ih": {
"id": ObjectId('ijiyt6ih'),
"name": "John",
"songs": [
{
"id": ObjectId('okoiu8yi'),
"songName": "Hello There",
"year": "1980"
},
{
"id": ObjectId('sewfd323'),
"songName": "No More",
"year": "1983"
}
]
},
"98usd96w": {
"id": ObjectId('98usd96w'),
"name": "Jack",
"songs": [
{
"id": ObjectId('iew342o3'),
"songName": "Hurry Up",
"year": "1985"
}
]
}
}
I need to find a way to appropriately loop through this so that I can modify the returned data to bind it to the viewModel using knockout.
Here is how my viewModel looks like:
singersViewModel = function(data) {
var self = {
singerId: ko.observable(data.id),
singerName: ko.observable(data.name),
songName: ko.observable(...),
songYear: ko.observable(...)
};
I am not sure if I will have to return two different sets of data or not.
As for the looping. I was able to loop and return the list of singers to display on the page but I am not able to get the list of songs displayed within each singer.
Here is my loop so far:
var self = {},
singer,
tempSingers = [];
self.singers = ko.observableArray([]);
for (singer in singers) {
if (singers.hasOwnProperty(singer)) {
tempSingers.push(new singersViewModel(singers[singer]));
}
}
self.singers(tempSingers);
I tried to duplicate the same type of loop for songs within this loop but i would get an error using hasOwnProperty because songs is an array.
In the included snippet you can see how you can map the original data to a viewmodel that can be bound to a view.
I've left the ids as regular properties, and converted the names into observables, so thatthey can be edited. At the bottom you can see the current viewmodel state.
There is also a sample view which iterates the list of singers, and also the list of song within each singer.
As you can see I'm implementing the solution using mapping. For mapping you need to implement a callback that receives each original object and returns a new one with a new structure. For example this part of the code
_.map(_singers, function(singer) {
return {
id: singer.id,
name: ko.observable(singer.name),
// ... songs:
})
iterates over each singer (the sample data in the question), and for each one creates a new object with the id, an observable which includes the name (and the mapping of songs, which I don't show in this fragment).
NOTE: I'm using lodash, but many browsers support map natively as an array function
var ObjectId = function (id) { return id; }
var singers = {
"ijiyt6ih": {
"id": ObjectId('ijiyt6ih'),
"name": "John",
"songs": [
{
"id": ObjectId('okoiu8yi'),
"songName": "Hello There",
"year": "1980"
},
{
"id": ObjectId('sewfd323'),
"songName": "No More",
"year": "1983"
}
]
},
"98usd96w": {
"id": ObjectId('98usd96w'),
"name": "Jack",
"songs": [
{
"id": ObjectId('iew342o3'),
"songName": "Hurry Up",
"year": "1985"
}
]
}
};
var SingersVm = function(_singers) {
var self = this;
self.singers = _.map(_singers, function(singer) {
return {
id: singer.id,
name: ko.observable(singer.name),
songs: _.map(singer.songs, function(song) {
return {
name: ko.observable(song.songName),
id: song.id
};
})
};
});
return self;
};
var vm = new SingersVm(singers);
//console.log(vm);
ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div data-bind="foreach: singers">
<div>
<input data-bind="value: name"/> (<span data-bind="text: id"></span>)
<ul data-bind="foreach:songs">
<li>
<input data-bind="value: name"/> (<span data-bind="text: id"></span>)
</li>
</ul>
</div>
</div>
<pre data-bind="html: ko.toJSON($root,null,2)">
</pre>

Categories

Resources