Kinda new here and new to javascript
So I am trying to make a chained select in javascript and I want to create different select from the value in the previous one, it seems easy to make two, but what about three selects or more?
Here's some of my code that I tried
function firstSelect(){
var x = document.getElementById('firstSelect').value;
switch(x){
case 'Food':
localStorage.setItem('first','food');
add(food, 'secondSelect');
document.getElementById('secondSelect').setAttribute('onchange', secondSelect());
//alert(x);
break;
//more code to be added
}
//trying to add a second select?
function secondSelect(){
var x = document.getElementById('secondSelect').value;
var first = localStorage.getItem('first');
if(first == 'food'){
for(i=0, i<food.length; i++){
if( x == food[i].value ){
add(food[i].value, 'thirdSelect');
}
}
}//more code to be added but this is the gist of it
}
//function to populate each select added
function add(itemArray, name){
if(document.getElementById(name)){
var element = document.getElementById(name);
element.parentNode.removeChild(element);
}
var list = document.createElement('select');
list.setAttribute('id', name);
var length = itemArray.length
var opt;
for(i=0;i<length;i++){
opt = document.createElement('option');
opt.setAttribute('value', itemArray[i].value);
opt.text = itemArray[i].text;
list.appendChild(opt);
}
document.body.appendChild(list);
}
//here's how my data looks like
var food = [
{value: "meat", text: "Meat"},
{value: "vege", text: "Vegetables"},
{value: "fruit", text: "Fruits"}
];
var meat = [
{value: "beef", text: "Beef"},
{value: "chicken", text: "Chicken"},
{value: "pork", text: "Pork"}
];
var vege = [
{value: "broccoli", text: "Broccoli"},
{value: "spinach", text: "Spinach"},
{value: "carrort", text: "Carrots"}
];
Basically I make different functions for the different level of select I make, not sure if I should do it like that but... if food is the first choice and meat/vege will be shown as the second select accordingly, so am I going to make another function for my third select(beef?chicken?pork?)
I was thinking about putting up one function for creating multiple selects but I am not sure how to write it as an onchange function, it seems to me that each onchange function for different select should be different too...?
Is there an efficient way of doing this?
Please
// This is just a map
// for selecting value for the child selector when the parent is changed.
// Parents value are the keys here
// the data to be passed to the child according to the current value of parent are stored in the array
// Map for first chain
const datamap_1 = {
1: [{
value: 10,
text: 'ten'
},
{
value: 12,
text: 'twelve'
}
],
2: [{
value: 20,
text: 'twenty'
},
{
value: 21,
text: 'twelve'
}
],
3: [{
value: 30,
text: 'thirty'
},
{
value: 31,
text: 'thirty one'
}
]
};
// Map for second chain chain
const datamap_2 = {
10: [{
value: 100,
text: 'hundred'
},
{
value: 101,
text: 'hundred and one'
}
],
20: [{
value: 200,
text: 'two hundred'
},
{
value: 201,
text: 'two hundred and one'
}
]
};
class ChainedSelect{
constructor (parent, child, map){
this.parent = parent;
this.child = child;
this.data_map = map;
this._attach_events();
this._init();
}
// Just for selecting the first value in parent selected by default and initlizing the child
_init(){
const val = this.parent.value;
this._make_options(this.data_map[val]);
}
// All the event handling goes here
_attach_events(){
this.parent.onchange = (function(_this){
return function(){
const val = this.value;
_this._make_options(_this.data_map[val]);
}
})(this)
}
// helper function for creating options from a passed array
_make_options(data){
this.child.innerHTML = '';
if (!data) return ;
for(let i=0;i < data.length;i++){
let opt = data[i];
let el = document.createElement("option");
el.textContent = opt.text;
el.value = opt.value;
this.child.appendChild(el);
this.child.dispatchEvent(new Event('change'));
}
}
}
window.onload = function(){
const first = document.getElementById('first');
const second = document.getElementById('second');
const third = document.getElementById('third');
const chain_1 = new ChainedSelect(first, second, datamap_1);
const chain_2 = new ChainedSelect(second, third, datamap_2);
}
here is a working jsfiddle https://jsfiddle.net/jgv8pd5k/
Related
I have the following function:
populateClinicRoomSelect(object) {
var selectArray = [];
var options = [];
for(var key in object) {
if(object.hasOwnProperty(key)) {
options = {
value: object[key].id,
label: object[key].RoomName,
};
selectArray = selectArray.concat(options);
}
}
return selectArray;
}
The idea is that takes two defined fields from the object array and places it in a new array. It works fine. I also have a few more functions exactly the same to this except the 'id' field and 'RoomName' field are different field names. Is there any way to pass 'id' and 'RoomName' as function variables to define them in the function?
Sure, you can pass field names as arguments and use [arg] accessors as you already do with [key]:
function populateClinicRoomSelect(object, valueField, labelField) {
var selectArray = [];
var options = [];
for(var key in object) {
if(object.hasOwnProperty(key)) {
options = {
value: object[key][valueField],
label: object[key][labelField],
};
selectArray = selectArray.concat(options);
}
}
return selectArray;
}
const object = {
a: {
id: 1,
room: 'first room'
},
b: {
id: 2,
room: 'second room'
}
}
const result = populateClinicRoomSelect(object, 'id', 'room');
console.log(result)
You mean like this?
function populateClinicRoomSelect(object, value, label) {
value = value || "id"; // defaults value to id
label = label || "RoomName"; // defaults label to RoomName
var selectArray = [];
var options = [];
for(var key in object) {
if(object.hasOwnProperty(key)) {
options = {
value: object[key][value],
label: object[key][label],
};
selectArray = selectArray.concat(options);
}
}
return selectArray;
}
let obj = { 1: { id:1, RoomName: "Blue Lounge" }, 2: { id:2, RoomName: "Red Lounge" } }
console.log(populateClinicRoomSelect(obj, 'id', 'RoomName'));
I have an api call that replies with an updated jSON object, I also have 1 static jSON object file. I am trying to compare a value in the object per teams with the same name.
So if Team John had 22 in the old file, and has 28 now, the new object should output Team John as 6. Subtracting the 2 and displaying the difference.
I have made a jsFiddle to help understand and update.
LATEST UPDATE: The answer has been solved by mscdeveloper! Check for his post and answer below.
UPDATE (not the answer): I have found a solution while searching in stackoverflow, this does EXACTLY what I want, but I lost the team's name in the process, how can I fix the code to where it doesn't delete it, I know it has something to do with the groupByTypeID function I have?
Updated jsFiddle: https://jsfiddle.net/kqmfsz9n/5/
var obj1 = {
"teams": [
{
name: 'Test 1',
numMembers: '50'
},
{
name: 'Test 2',
numMembers: '12'
}
]
};
var obj2 = {
"teams": [
{
name: 'Test 1',
numMembers: '75'
},
{
name: 'Test 2',
numMembers: '18'
}
]
};
var newObj = {};
function groupByTypeID(arr) {
var groupBy = {};
jQuery.each(arr, function () {
groupBy[this.name] = parseInt(this.numMembers);
});
return groupBy;
}
var userArrayGroups = groupByTypeID(obj2.teams);
var origArrayGroups = groupByTypeID(obj1.teams);
var newObj = [];
for (var prop in userArrayGroups) {
newObj[prop] = userArrayGroups[prop] - origArrayGroups[prop];
newObj.push(userArrayGroups[prop] - origArrayGroups[prop]);
if (newObj[prop] == 0) {
delete newObj[prop];
}
}
console.log(newObj);
All help is appreciated!
Thank you.
var obj1 = {
"teams": [
{
name: 'Test 1',
numMembers: '50'
},
{
name: 'Test 2',
numMembers: '12'
}
]
};
var obj2 = {
"teams": [
{
name: 'Test 1',
numMembers: '75'
},
{
name: 'Test 2',
numMembers: '18'
}
]
};
var newObj = {};
var items_arr=[]; //array of obj2 not exist in obj1
if(obj1.teams){ //if exist array of teams obj1
var o1teams = obj1.teams;
if(obj2.teams){ //if exist array of teams obj2
var o2teams = obj2.teams;
for(var key2 in o2teams){
var o2teams = obj2.teams;
for(var key1 in o1teams){
if(o2teams[key2].name==o1teams[key1].name){
var numMembers_o1_int=parseInt(o1teams[key1].numMembers)||0;
var numMembers_o2_int=parseInt(o2teams[key2].numMembers)||0;
var result_numMembers_int=numMembers_o2_int-numMembers_o1_int;
var result_numMembers=result_numMembers_int+''; //convert to string
var items_for_add=o1teams[key1];
items_for_add.numMembers=result_numMembers;
items_arr.push(items_for_add);
}
}
}
}
}
newObj.items=items_arr;
console.log(newObj);
https://jsfiddle.net/mscdeveloper/uxv1t2a7/3/
I have this array and variable:
var freqId = 45;
$scope.frequencies = [{Id:124,name:'qqq'},
{Id:589,name:'www'},
{Id:45,name:'eee'},
{Id:567,name:'rrr'}]
I use this row to get all id's from array above:
var inspectionsId = $scope.frequencies.map(function (obj) { return obj.Id; })
The result I get is:
var Id's = [124,589,45,567];
I need to change this row:
$scope.frequencies.map(function (obj) { return obj.Id; })
to retrive all id from frequencies array except where Id equal to freqId variable.
For example desired result is:
var inspectionsId = [124,589,567];
Any idea how can I implemet it?
You can also use Array.prototype.reduce to do both filtering and mapping in a single loop:
var freqId = 45;
$scope = {}; // Dummy scope
$scope.frequencies = [{
Id: 124,
name: 'qqq'
}, {
Id: 589,
name: 'www'
}, {
Id: 45,
name: 'eee'
}, {
Id: 567,
name: 'rrr'
}]
var result = $scope.frequencies.reduce(function(result, current) {
if (current.Id != freqId) {
result.push(current.Id);
}
return result;
}, []);
console.log(JSON.stringify(result));
map is designed to transform data, not filter it. Chain it with filter for the latter.
var freqId = 45;
var input = [{
Id: 124,
name: 'qqq'
}, {
Id: 589,
name: 'www'
}, {
Id: 45,
name: 'eee'
}, {
Id: 567,
name: 'rrr'
}];
var output = input.map(function(obj) {
return obj.Id;
}).filter(function(element) {
return element != freqId
});
console.log(output);
You can use Array.prototype.filter:
var inspectionsId = $scope.frequencies
.map(function(obj) { return obj.Id; })
.filter(function(id) { return id !== 45 })
You'll have seen the filter answers. 99.999% of the time, that's the way to go.
If you have a truly massive array and you think it's important to make just a single pass through it, you could give yourself a function combining map and filter:
// A value to use to mean "leave entry out"
Object.defineProperty(Array.prototype, "OMIT", {
value: {}
});
// The utility function
Object.defineProperty(Array.prototype, "mapFilter", {
value: function(f, thisArg, omissionFlag) {
var result = [];
if (arguments.length < 3) {
omissionFlag = Array.OMIT;
}
Object.keys(this).forEach(function(index) {
var value = f.call(thisArg, this[index], index, this);
if (value !== omissionFlag) {
result.push(value);
}
}, this);
return result;
}
});
// Using it
var freqId = 45;
var input = [{Id: 124, name: 'qqq'}, {Id: 589, name: 'www'}, {Id: 45, name: 'eee'}, {Id: 567, name: 'rrr'}];
var output = input.mapFilter(function(obj) {
return obj.Id == freqId ? Array.OMIT : obj.Id;
});
console.log(output);
This version accepts up to three arguments:
The map/filter function
The value to use as this during callbacks
The value to use to mean "omit this entry," which defaults to Array.OMIT
It calls its callback with the value, index, and array just like forEach and map and such do.
Again, though, I'll emphasize that in the vast, vast majority of cases, filter and then map (or map and then filter if the map makes filtering easier) is the way to go.
That said, a generic "loop with memo" function has broader applicability:
// The utility function
Object.defineProperty(Array.prototype, "memoLoop", {
value: function(memo, f, thisArg) {
Object.keys(this).forEach(function(index) {
f.call(thisArg, memo, this[index], index, this);
}, this);
return memo;
}
});
// Using it
var freqId = 45;
var input = [{Id: 124, name: 'qqq'}, {Id: 589, name: 'www'}, {Id: 45, name: 'eee'}, {Id: 567, name: 'rrr'}];
var output = input.memoLoop([], function(result, obj) {
var id = obj.Id;
if (id != freqId) {
result.push(id);
}
});
console.log(output);
It's a bit like Array#reduce but assumes an unchanging memo value (in our case, the new array), which simplifies the callback somewhat.
var set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
I'd like to be able to do something like a db call, set.find({"color":"green"}) and have it return an array full of objects that contain that property.
Using Array#filter, for this particular case the code would look like
var results = set.filter(function (entry) { return entry.color === "green"; });
Array#filter is not implemented in some older browsers, so see the linked article for a backward compatibility shim, or better yet get a full-fledged ES5 shim.
For the more general case, it's just a matter of extending this idea:
function findByMatchingProperties(set, properties) {
return set.filter(function (entry) {
return Object.keys(properties).every(function (key) {
return entry[key] === properties[key];
});
});
}
var results = findByMatchingProperties(set, { color: "green" });
Again, I am using ECMAScript 5 methods Object.keys and Array#every, so use an ES5 shim. (The code is doable without an ES5 shim but uses manual loops and is much less fun to write and read.)
I have used map function from jquery and I am getting selected index by passing searched key value so by using that index we will get required object from array.
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var mydata = [{ name: "Ram", Id: 1 }, { name: "Shyam", Id: 2 }, { name: "Akhil", Id: 3 }];
searchKey = 2
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
var selectedData = mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey)];
console.log(selectedData)
output
{ name: "Shyam", Id: 2 }
Note: if you want to pass search key as object then
searchKey = { Id: 2 };
mydata[mydata.map(function (item) { return item.Id; }).indexOf(searchKey.Id)];
output
{ name: "Shyam", Id: 2 }
Using arrow functions with an implied return and concise body:
const results = set.filter(entry => entry.color === "green");
Another example passing in a search variable:
const searchString = 'green';
const results = set.filter(entry => entry.color === `${searchString}`);
Read more about arrow functions on
MDN
Since you've included the jQuery tag, here's one way to do it using jQuery's map:
var results = $.map( set, function(e,i){
if( e.color === 'green' ) return e;
});
The documentation states that you need to return null to remove the element from the array, but apparently this is false, as shown by the jsFiddle in the comments; returning nothing (i.e. returning undefined) works just as well.
I went with a different approach that I found to be a bit easier.
function isObjEqual(a, b) {
const x = JSON.stringify(a);
const y = JSON.stringify(b);
return x === y;
}
// Example 1
const set = [{"color":"blue"},{"color":"green"},{"color":"red"},{"color":"green"}];
const findObj1 = {"color":"green"};
const arr1 = set.filter((objInArr) => isObjEqual(objInArr, findObj1));
console.log(arr1) // [ { color: 'green' }, { color: 'green' } ]
// Example 2
const list = [{
"label": "Option 2",
"value": "option2"
},
{
"label": "Option 3",
"value": "option3"
},
{
"label": "Option 2",
"value": "option2"
}
];
const findObj2 = {
"label": "Option 2",
"value": "option2"
}
const newList = list.filter((objInArr) => isObjEqual(objInArr, findObj2));
console.log(newList) //[ { label: 'Option 2', value: 'option2' }, { label: 'Option 2', value: 'option2' } ]
I have an array of javascript objects like the following:
var food = [
{id: 1, name: 'Apples', owned: true },
{id: 2, name: 'Oranges', owned: false },
{id: 3, name: 'Bananas', owned: true }
];
Then I receive another array with the following data:
var newFood = [
{id: 1, name: 'Peas'},
{id: 2, name: 'Oranges'},
{id: 3, name: 'Bananas'},
{id: 4, name: 'Grapefruits'}
];
How can I update the previous food array with the new information in newFeed, without overwriting the original owned property, while adding an owned: false to any new object?
Keep in mind this is plain javascript, not jQuery.
You'd probably want to index food by id so make food an object instead of an array:
var food = {
1: {name: "Apples", owned: true},
//...
}
then iterate over newFood and update the fields appropriately.
I think you can use underscore.js for fix the problem.
var arrayObj = [
{Name:'John',LastName:'Smith'},
{Name:'Peter',LastName:'Jordan'},
{Name:'Mike',LastName:'Tyson'}
];
var element = _.findWhere(arrayObj, { Name: 'Mike' });
element.Name="SuperMike";
console.log(arrayObj);
This works:
var temp = {};
for (var i = 0, l = food.length; i < l; i += 1) {
temp[food[i].name] = true;
}
for (var i = 0, l = newFood.length; i < l; i += 1) {
if ( !temp[newFood[i].name] ) {
food.push( { id: food.length + 1, name: newFood[i].name, owned: false });
}
}
The first for statement will populate the temp object with the fruit names from the food array, so that we know which fruits exist in it. In this case, temp will be this:
{ "Apples": true, "Oranges": true, "Bananas": true }
Then, the second for statement checks for each fruit in newFood if that fruit exists in temp, and if it doesn't, if pushes a new array item into the food array.
some thing like this? JSFiddle Example
JavaScript
function updateFood( newFood, oldFood ) {
var foodLength = oldFood.length - 1;
for (var i = 0; i < newFood.length; i++) {
if (i > foodLength) { //add more if needed
newFood[i].owned = false;
oldFood.push(newFood[i]);
} else if (!food[i].owned) { //replace if needed
newFood[i].owned = false;
oldFood[i] = newFood[i];
}
}
}