How to avoid multiple map functionality - javascript

I have two objects. First one have entire school full details of students record. Example like
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
Second one have particular class students information about the status of the student for that day. Example like
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
Now I need to compare the student id and need to display the status based on the result. I have achieved in the following way.
items = first.students.map(function(item){
status =item.status;
second.students.map(function(key){
if(key.id == item.id) { status = key.status }
});
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+status+"</td></tr>";
});
$('table#main tbody').html(items);
The above code is working fine. But if you look at my code, I have used the map functionality multiple times. I feel that I have done something wrong in the performance wise. Is that possible to reduce using the map twice or any other better way to achieve the same result. Please suggest me.
Code Snippet
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
items = first.students.map(function(item){
status =item.status;
second.students.map(function(key){
if(key.id == item.id) { status = key.status }
});
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+status+"</td></tr>";
});
$('table#main tbody').html(items);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="main" cellspacing="2" border="1">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Degree</th>
<th>Stauts</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</tbody>
</table>

Due to the way your objects are set up, it looks like that will be O(n) time for the lookup, because you need to loop through the first student array for every student id.
To get around this, you can do a single map where you assign the id as the key of a new intermediate object in the format of:
x = {1: {...}, 2: {...}}
From there, you can now do constant time O(1) lookups:
x[id]
The only extra work is building the intermediate hash, but that will be less computation than what you have above.
See this example below. Note that it does use 2 maps, but it's different than your example because it's not a map within a map which is exponential:
var students = [
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
];
var studentIds = {};
students.forEach(function(student) {
studentIds[student.id] = {name: student.name, age: student.age, degree: student.degree, status: student.status}
});
var second = [
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
];
var studentStatuses = second.map(function(student) {
// do whatever you have to do here
return (studentIds[student.id] || {}).status;
});

The complexity will be better if you build an object which keys are id and values are status from second.students then you update status in first.students based on this object:
var first = {
students:
[
{ id:'1', name:"suresh", age:"20", degree:"BSc", status:"obsent"},
{ id:'2', name:"ramesh", age:"21", degree:"BCom", status:"present"},
{ id:'3', name:"rajesh", age:"19", degree:"BA", status:"leave"},
{ id:'4', name:"satish", age:"28", degree:"BL", status:"obsent"}
]
}
var second ={
students:
[
{ id:'1',status:"present"},
{ id:'12',status:"obsent"},
{ id:'3',status:"obsent"},
{ id:'14',status:"leave"}
]
}
var statusById= second.students.reduce(function(m, e) {
m[e.id] = e.status;
return m;
}, {});
items = first.students.map(function(item){
item.status = statusById[item.id] || item.status;
return "<tr><td>"+item.name+"</td><td>"+item.age+"</td><td>"+item.degree+"</td><td>"+item.status+"</td></tr>";
});
$('table#main tbody').html(items);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table id="main" cellspacing="2" border="1">
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Degree</th>
<th>Stauts</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</tbody>
</table>

Related

How would I create a timesheet and tie each input value to the respective date?

I wish to create a timesheet grid. Basically, a grid that displays the current week and the project you're working on as such: https://jsfiddle.net/ho9a8neq/
How do I set up v-model so I can correctly send the value with the corresponding date to a database?
Something like
[
{date: "jan-1": 8, project: 1},
{date: "jan-2": 10, project: 1},
{date: "jan-3": 10, project: 2}
]
To be able to collect data for different projects during a timeline and track the day of each one the solution I propose consists in the following data structure:
day = ''
week = [day, day, day]
project = [week, week, ...]
For the sake of simplicity it limits to adding future weeks. Its possible to change this, but it will require more complexity on the models and I don't think it will help to understand how to bind data to the model.
For each week there must be a model that retains each day data, this is done creating an array of empty strings:
week: ['','','','','','','']
Each project can contain several weeks:
data: [week, week, week]
When the user creates a new project it must replicate the project model considering the current week:
_.cloneDeep(project(this.weekNum, this.rows.length))
Now, with the data structure in place, bind the view to it:
<input type="text" style="width: 35px;" v-model="row.data[weekNum][i]">
See snippet to understand how everything ties together:
const weekData = () => new Array(7).fill('');
const project = (weekNum, id) => ({
project: "first",
id,
data: Array(weekNum + 1).fill([]).map(weekData)
});
new Vue({
el: "#app",
data: {
weekNum: 0,
rows: [_.cloneDeep(project(0, 0))]
},
methods: {
addProject() {
window.pp = _.cloneDeep(
project(this.weekNum, this.rows.length)
)
this.rows.push(
window.pp
);
},
deleteRow(key) {
this.rows.splice(key, 1);
},
nextWeek() {
this.weekNum++;
this.rows.forEach(_project => {
if (!_project.data[this.weekNum]) {
_project.data.push(weekData());
}
});
},
prevWeek() {
this.weekNum--;
this.rows.forEach(row => {
if (!row.data[this.weekNum]) {
row.data.unshift(weekData());
}
});
},
dates(dateFormat, weekNumber) {
let startOfWeek = moment().startOf('week').add(weekNumber, "week");
const endOfWeek = moment().endOf('week').add(weekNumber, "week");
const days = [];
while (startOfWeek <= endOfWeek) {
days.push(startOfWeek.format(dateFormat))
startOfWeek = startOfWeek.clone().add(1, 'd');
}
return days
},
log() {
const output = _.reduce(this.rows, (result, row) => {
const project = {
project: row.id
};
const startWeek = moment().startOf('week');
const weekDays = [];
row.data.forEach((week, weekIdx) => {
week.forEach((data, dataIdx) => {
if (data === '') return;
weekDays.push({
data,
project: row.id,
day: startWeek.clone().add(weekIdx, 'week').add(dataIdx, 'd').format('MMM D')
});
});
});
return [...result, ...weekDays];
}, []);
console.log(output)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/moment#2.24.0/moment.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.11/lodash.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.22/dist/vue.min.js"></script>
<div id="app">
<!-- for the sake of simplicity limit the date to future weeks -->
<button #click="prevWeek" :disabled="weekNum < 1">Previous week</button>
<button #click="nextWeek">Next week</button>
<button #click="addProject">Add project</button>
<table>
<tr>
<th>Project</th>
<th v-for="(day, i) in dates('MMM D', weekNum)" :key="i">{{day}}</th>
</tr>
<tbody>
<tr v-for="(row, key) in rows" :key="key">
<td>Project {{key}}</td>
<td v-for="(n,i) in dates('YYYY-MM-DD', weekNum)" :key="`${row.id}-${i}`">
<input type="text" style="width: 35px;" v-model="row.data[weekNum][i]">
</td>
<td><button #click="deleteRow(key)">x</button></td>
</tr>
</tbody>
</table>
<button #click="log()">log</button>
</div>

I want to check all table rows in Ember.js

I want to check all table rows and if all values are same alert('ok) in Ember.js
Here is my Code.
actions:{
checkValue: function(){
var r = this.$('.cellValue').text();
if(r == '|'){
console.log('ok');
}else{
console.log('bad');
}
}
}
I'd suggest you're going about this problem in a non-Ember way. Why don't you try something like this:
Component.js
import Ember from 'ember';
export default Ember.Component.extend({
rows: [
{name: 'bob', value: "24"},
{name: 'peter', value: "32"}
],
checkValue: Ember.observer('rows.#each.value', function () {
const unique = Ember.get(this, 'rows').uniqBy('value')
if (unique.length === 1) alert('matching')
})
});
Template.hbs
<table>
{{#each rows as |row|}}
<tr>
<td>{{row.name}}</td><td><input value={{row.value}} onblur={{action (mut row.value) value='target.value'}}></td>
</tr>
{{/each}}
</table>
There is a Twiddle here

Meteor - Merging Data from multiple Collections

I have been doing lots of search on how to merge data from multiple collections and then send it to the Template using the Template Helper Function client side.
I referred to the solution given at : Merging collections in Meteor
Below is my Code:
Template HTML :
<template name="search_results">
<div class="col-md-12">
{{#if Template.subscriptionsReady}}
<table id="datatable" class="display" cellspacing="0" width="100%">
<thead>
<tr>
<th>Type</th>
<th>Project Name</th>
<th>_id</th>
</tr>
</thead>
<tbody>
{{#each SearchData}}
<tr>
<td>{{search_type}}</td>
<td>{{name}}</td>
<th>{{_id}}</th>
</tr>
{{/each}}
</tbody>
</table>
{{else}}
<div class="loading">{{>spinner}}</div>
{{/if}}
</div>
</template>
Template Helper Function:
Template.search_results.helpers({
SearchData: function() {
var project_name = Router.current().params.query.search;
var Projects = Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; }).fetch();
var CustomerAccounts = CustomerAccounts.find({account_name: { $regex : project_name, $options:"i" } }).map( function ( CustomerAccount ) { return { _id: CustomerAccount._id, name: CustomerAccount.account_name, search_type: 'Accounts' }; }).fetch();
var docs = Projects.concat(CustomerAccounts);
return docs;
}
});
I have tried the following code to send data from the single collection and its working fine:
return Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; });
But I want to merge two collections and send the data to Template, Where I am wrong and what is the good way to do it?
A cursor's map function returns an array, so calling fetch on that array would throw an error. Try removing the fetch calls and see if that helps.
I was doing one Typo Mistake Now the Issue is corrected:
Check My Rectified Code Below
var projects = Projects.find({project_name: { $regex : project_name, $options:"i" } }).map( function ( Project ) { return { _id: Project._id, name: Project.project_name, search_type: 'Projects' }; });
var customeraccounts = CustomerAccounts.find({account_name: { $regex : project_name, $options:"i" } }).map( function ( CustomerAccount ) { return { _id: CustomerAccount._id, name: CustomerAccount.account_name, search_type: 'Accounts' }; });
var docs = projects.concat(customeraccounts);
return docs;
I have created variable name same as Collection Name, then reailsed and then changed it to lowercase, instead of var Projects I changed it to var projects,
Second thing I have also removed fetch()
The issue is Solved now , As David Suggested me to remove fetch() , I am marking his Answer as Correct although my code has another issue of variable name.

ng-repeat filter data in angularjs not working

I have a data setup which looks like this
$scope.friends = [
{
name: function() {
return 'steve';
},
number: function() {
return 555;
}
},
{
name: function() {
return 'mary';
},
number: function() {
return 555;
}
},
{
name: function() {
return 'jo';
},
number: function() {
return 888;
}
}
]
Now what I want is to filter data on ng-repeat based on text input.To achieve this I have tried like this:
<table id="searchTextResults">
<tr><th>Name</th><th>Phone</th></tr>
<tr ng-repeat="friend in friends | filter:search">
<td>{{friend.name}}</td>
<td>{{friend.number}}</td>
</tr>
</table>
Where input is defined like <input type="text" ng-model="search.name"/>.
But the filter does not work.I have tried and come to see if I modify the data structure like this
$scope.friends = [
{
name:'steve',
number: 555
},
{
name:'marry',
number: 555
},
{
name:'steve',
number: 888
}
]
then only this works.However I want to keep my data structure as it is.
Can anyone please help on this how can I filter having the above data structure.
plnkr example.
You can modify the friends data to achieve this requirement.
What I have done is using angular.forEach() function to modify the data and put it into a new array called $scope.modifiedFriends.
$scope.modifiedFriends = [];
angular.forEach($scope.friends, function(friend) {
$scope.modifiedFriends.push({
name: friend.name(),
number: friend.number()
});
});
Instead of ng-repeat on friends, I ng-repeat of modifiedFriends.
<tr ng-repeat="friend in modifiedFriends | filter:search">
<td>{{friend.name}}</td>
<td>{{friend.number}}</td>
</tr>
Working Plunker

Add item to array Angular

I have a table with these fields: product, lot, input1, input2. You can clone a line, and you can add a new line.
What I want to do is that for each row you can add a new Lot created by a "number" and by "id" that user write in the input field under the Select lot. And I wanted that the script add the new Lot in the json data and the lot 's option list.
This is the function for add that I tried to do:
$scope.addLot = function() {
var inWhichProduct = row.selectedProduct;
var newArray = {
"number": row.newLot.value,
"id": row.newLot.id
};
for (var i = 0; i < $scope.items.length; i++) {
if ($scope.items[i].selectedProduct === inWhichProduct) {
$scope.items[i].selectedLot.push(newArray);
}
}
};
-->> THIS <<-- is the full code.
Can you help me?
I think your question is a little too broad to answer on Stack Overflow, but here's an attempt:
<div ng-app="myApp" ng-controller="myCtrl">
<table>
<tr ng-repeat="lot in lots">
<td>{{ lot.id }}</td>
<td>{{ lot.name }}</td>
</tr>
</table>
<p>name:</p> <input type="text" ng-model="inputName">
<p>id:</p> <input type="text" ng-model="inputId">
<button ng-click="addLotButton(inputId, inputName)">Add</button>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0-beta.2/angular.min.js"></script>
<script>
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.lots = [{
name: "test",
id: 1
},
{
name: "test2",
id: 2
}
];
$scope.addLot = function(lotId, lotName) {
var newLotObject = {
name: lotName,
id: lotId
};
$scope.lots.push(newLotObject);
};
$scope.addLotButton = function(id, name) {
$scope.addLot(id, name);
};
$scope.addLot(3, "Another test");
});
</script>
Basically this code just takes some input and adds an object to the scope for that input. The table is created using an ng-repeat of this data. It's not great code at all but it's just a quick example.
The push method adds newArray to selectedLot array. It's not working on the JSON data but on arrays. If you want to have the JSON, you can give a try to :
var myJsonString = JSON.stringify(yourArray);
It will create a JSON string based on the parameter
Maybe you should try to structure your data to make lots as properties of products.
{
products: [
{id: 1, lots: [{id:1}, {id:2}]},
{id: 2, lots: [{id:1}, {id:2}]}
]
}
To add a lot to a product :
product = products[0];
product.lots.push(newArray);
Change the fallowing:
html:
<button ng-click="addLot(row.selectedProduct.id,row.newLot.value,row.newLot.id)">Add</button>
js:
$scope.addLot = function(id,val,lotId) {
// console.log(id);
var inWhichProduct = id;
var newArray = { "value": val, "id": lotId };
//console.log($scope.items)
angular.forEach($scope.items,function(v,i){
if($scope.items[i].id == id )
{
$scope.items[i].lots.push(newArray);
console.log($scope.items[i].lots);
}
});
};
http://plnkr.co/edit/W8eche8eIEUuDBsRpLse?p=preview

Categories

Resources