I have the following spans inside a DIV
<div class="itens">
<span data-type="car" data-value="1"></span>
<span data-type="car" data-value="2"></span>
<span data-type="car" data-value="3"></span>
<span data-type="bus" data-value="1"></span>
<span data-type="bus" data-value="2"></span>
<span data-type="airplane" data-value="1"></span>
</div>
And I need to iterate through the spans and group by type. In the end, I need to have a hash like this
{
car: [1,2,3],
bus: [1, 2],
airplane: [1]
}
I've tried some snippets but I can't solve this.
Thanks.
You can use the jQuery [attribute=value] selector for each data type, then iterate each one with .each
const store = {
car: [],
bus: [],
airplane: [],
};
$("[data-type='car']").each(function() {
store.car.push(this.getAttribute('data-value'));
});
$("[data-type='bus']").each(function() {
store.bus.push(this.getAttribute('data-value'));
});
$("[data-type='airplane']").each(function() {
store.airplane.push(this.getAttribute('data-value'));
});
For a more general case, we want to initialize the arrays as we discover types that don't have one yet.
const store = {};
$(".items span").each(function() {
let dataType = this.getAttribute('data-type');
//see if the array for the type has been defined yet
if(!store.hasOwnProperty(dataType)) {
store[dataType] = [];
}
store[dataType].push(this.getAttribute('data-value'));
});
Try using this Javascript Code -
var resultArr = new Object();
$('div.itens span').each(function(key, val){
var typeVal = $(val).attr('data-type');
var value = $(val).attr('data-value');
if(resultArr.hasOwnProperty(typeVal)){
resultArr[typeVal].push(value);
}
else{
resultArr[typeVal] = [value];
}
});
Related
I'm trying to match and group objects, based on a property on each object, and put them in their own array that I can use to sort later for some selection criteria. The sort method isn't an option for me, because I need to sort for 4 different values of the property.
How can I dynamically create separate arrays for the objects who have a matching property?
For example, I can do this if I know that the form.RatingNumber will be 1, 2, 3, or 4:
var ratingNumOne = [],
ratingNumTwo,
ratingNumThree,
ratingNumFour;
forms.forEach(function(form) {
if (form.RatingNumber === 1){
ratingNumOne.push(form);
} else if (form.RatingNumber === 2){
ratingNumTwo.push(form)
} //and so on...
});
The problem is that the form.RatingNumber property could be any number, so hard-coding 1,2,3,4 will not work.
How can I group the forms dynamically, by each RatingNumber?
try to use reduce function, something like this:
forms.reduce((result, form) => {
result[form.RatingNumber] = result[form.RatingNumber] || []
result[form.RatingNumber].push(form)
}
,{})
the result would be object, with each of the keys is the rating number and the values is the forms with this rating number.
that would be dynamic for any count of rating number
You could use an object and take form.RatingNumber as key.
If you have zero based values without gaps, you could use an array instead of an object.
var ratingNumOne = [],
ratingNumTwo = [],
ratingNumThree = [],
ratingNumFour = [],
ratings = { 1: ratingNumOne, 2: ratingNumTwo, 3: ratingNumThree, 4: ratingNumFour };
// usage
ratings[form.RatingNumber].push(form);
try this its a work arround:
forms.forEach(form => {
if (!window['ratingNumber' + form.RatingNumber]) window['ratingNumber' + form.RatingNumber] = [];
window['ratingNumber' + form.RatingNumber].push(form);
});
this will create the variables automaticly. In the end it will look like this:
ratingNumber1 = [form, form, form];
ratingNumber2 = [form, form];
ratingNumber100 = [form];
but to notice ratingNumber3 (for example) could also be undefined.
Just to have it said, your solution makes no sense but this version works at least.
It does not matter what numbers you are getting with RatingNumber, just use it as index. The result will be an object with the RatingNumber as indexes and an array of object that have that RatingNumber as value.
//example input
var forms = [{RatingNumber:5 }, {RatingNumber:6}, {RatingNumber:78}, {RatingNumber:6}];
var results = {};
$.each(forms, function(i, form){
if(!results[form.RatingNumber])
results[form.RatingNumber]=[];
results[form.RatingNumber].push(form);
});
console.log(results);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
HIH
// Example input data
let forms = [{RatingNumber: 1}, {RatingNumber: 4}, {RatingNumber: 2}, {RatingNumber: 1}],
result = [];
forms.forEach(form => {
result[form.RatingNumber]
? result[form.RatingNumber].push(form)
: result[form.RatingNumber] = [form];
});
// Now `result` have all information. Next can do something else..
let getResult = index => {
let res = result[index] || [];
// Write your code here. For example VVVVV
console.log(`Rating ${index}: ${res.length} count`)
console.log(res)
}
getResult(1)
getResult(2)
getResult(3)
getResult(4)
Try to create an object with the "RatingNumber" as property:
rating = {};
forms.forEach(function(form) {
if( !rating[form.RatingNumber] ){
rating[form.RatingNumber] = []
}
rating[form.RatingNumber].push( form )
})
I want to find strings that has data from the strings from the array 2 in the array1 and save result as separate uniq array.
As can you see I search for not exact values. From the array1 values I know only part of the information, and I want to find the complete strings, with that information, in array1. And at the end I want to save what I found. So, I don't have a problem with finding here, but a problem with saving in the valid single JSON.
Array examples:
Array #1:
{
"overflow": [
"id:address:name:location:email",
...
"id2:address2:name2:location2:email2"
]
}
Array #2:
[
"location:email",
...
"location2:email2"
]
Code:
resultArr: function() {
var arr1 = '/var/log/1.json';
var arr2 = '/var/log/2.json';
var arrResult = '/var/log/result.json';
var arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8'));
for (var i = 0; i < arr2Obj.length; i++) {
var arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8'));
arr1Obj.overflow = arr1Obj.overflow.filter(function(e) {
return e.includes(arr2Obj[i])
});
fs.appendFile(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8');
}
}
My result:
[{
"overflow": [
"id:address:name:location:email"
]
}{
"overflow": [
"id54:address54:name54:location54:email56"
]
}{
"overflow": [
"id2:address2:name2:location2:email2",
"id6:address6:name6:location2:email2"
]
}
What I really want:
{
"overflow": [
"id:address:name:location:email",
"id54:address54:name54:location54:email56",
"id6:address6:name6:location2:email2",
"id2:address2:name2:location2:email2"
]
}
Instead of reading the file again and again, and appending to the result repeatedly, just do both actions only once. All the rest should happen in memory.
You will also get better results (no risk for duplicates in result) when you swap the loops: put the filter action as the outer loop. For the inner loop you can use some, since one match is enough for the entry to be included:
resultArr: function() {
var arr1 = '/var/log/1.json',
arr2 = '/var/log/2.json',
arrResult = '/var/log/result.json',
arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8')),
arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8'));
arr1Obj.overflow = arr1Obj.overflow.filter(function(e) {
return arr2Obj.some(function (f) {
return e.includes(f)
});
});
fs.writeFileSync(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8');
}
At each iteration, you're creating a new object and appening it to a file.
JSON is not a good format to append to.
You're replacing the array instead of adding fields to it.
You can do it that way, it should work :
resultArr: () => {
let arr1 = '/var/log/1.json';
let arr2 = '/var/log/2.json';
let arrResult = '/var/log/result.json';
let arr2Obj = JSON.parse(fs.readFileSync(arr2, 'utf-8'));
let arr1Obj = JSON.parse(fs.readFileSync(arr1, 'utf-8')); // reading only one time
arr1Obj.overflow = arr2Obj.map(value => {
return arr1Obj.overflow.filter(e => return e.includes(value))
});
fs.writeFileSync(arrResult, JSON.stringify(arr1Obj, null, 2), 'utf-8'); //Writing only one time
}
Array.map() executes the closure for each field in your array and group all the values returned by the closure in another array.
I also replaced some keywords to make your code more ES6 compliant. I you really want to append, you should use CSV and not JSON.
I have the following code with more values, showing only 3 to give you an idea
var people = {
'Jon':{
age:65,
height:185,
marriage:true
},
'Mary':{
age:18,
height:170,
marriage:false
},
'Carol':{
age:45,
height:165,
marriage:true
}
};
Because now I get all the values dynamically from the server, I would like to replicate the object to get the dynamic values from the <a> tag instead listing all of them above.
<a data-name='${person.name}' data-age="${person.age}" data-height="${person.height}" data-marriage="${person.isMarried}" href='/#'>
<script>
var personName = $('a').data('name');
var personAge = $('a').data('age');
var personHeight = $('a').data('height');
var isMarried = $('a').data('marriage');
</script>
I am trying something like this, but it doesn't seem to work, do i need to create a loop, not really sure
var people = {
personName:{
age:personAge,
height:personHeight,
marriage:isMarried
}
};
Any help will be appreciated
Thanks
Yes. You will need a loop (or equivalent). Here is a simple working approach.
var people = {};
$('a').each(function(){
var a = $(this);
people[a.data('name')] = {
age: a.data('age'),
height: a.data('height'),
marriage: a.data('marriage')
}
});
document.body.innerHTML += JSON.stringify(people, null, 2);
<script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
<a data-name='Jon' data-age="65" data-height="185" data-marriage="true" href='/#'></a>
<a data-name='Mary' data-age="age" data-height="170" data-marriage="false" href='/#'></a>
<a data-name='Carol' data-age="45" data-height="165" data-marriage="true" href='/#'></a>
If you didn't want to use jQuery, here's a simple vanilla way of doing it, making sure that the data types are what you require in your output.
var anchors = [].slice.call(document.getElementsByTagName('a'));
var people = {};
anchors.forEach(function (el) {
people[el.getAttribute('data-name')] = {
age: +el.getAttribute('data-age'),
height: +el.getAttribute('data-height'),
marriage: el.getAttribute('data-marriage') === 'false' ? false : true
};
});
people output
{
"Jon": {
"age": 65,
"height": 185,
"marriage": false
},
"Mary": {
"age": 18,
"height": 170,
"marriage": false
},
"Carol": {
"age": 40,
"height": 165,
"marriage": true
}
}
DEMO
You need to create a loop, but it's better if the server that returns this kind of object return an array.
This code will do what you want.
var peopleArray=[];
for (var i in people) {
if(people.hasOwnProperty(i) {
var currentPeople = people[i];
currentPeople.name = i;
peopleArray.push(currentPeople);
})
}
This code create an array of people like this :
[
{
name:'Jon',
age:65,
height:185,
marriage:true
},
{
...
}
]
It seems you want to create an object by reading the data-* attributes of the a elements. If this is the case one option is:
var people = {};
$('a').each(function() {
var data = $(this).data(), name = data.name;
delete data.name;
people[name] = data;
});
If you want to create an array of objects you can use the $.prototype.map method:
var people = $('a').map(function() { return $(this).data() }).get();
// [{
// "name": "${person.name}",
// "age": "${person.age}",
// "height": "${person.height}",
// "marriage": "${person.isMarried}"
// }]
i have a model like this
function ViewModel(){
var self = this
self.Choices = ko.observableArray([])
self.AcceptedChoices = ko.observableArray([])
self.LoadData = function(){
self.ViewAnswered()
}
self.ViewAnswered = function(){
var url = 'QuestionsApi/ViewAnswered'
var type = 'GET'
ajax(url , null , self.OnViewAnsweredComplete, type )
}
self.OnViewAnsweredComplete = function(data){
var currentAnswer = data.Answer
self.Choices(currentAnswer.Choices)
self.AcceptedChoices(currentAnswer.AcceptedChoices)
}
self.LoadData()
}
Here is my object. I have removed extra things
{
"AcceptedChoices": [94, 95],
"Choices": [{
"ChoiceId": 93,
"ChoiceText": "Never"
}, {
"ChoiceId": 94,
"ChoiceText": "Sometimes"
}, {
"ChoiceId": 95,
"ChoiceText": "Always"
}]
}
And here is binding
<u data-bind="foreach:Choices">
<li>
<input type="checkbox" name="choice[]" data-bind="value:ChoiceId,checked:$root.AcceptedChoices">
<span data-bind="text:ChoiceText">Never</span>
</li>
</u>
Now the problem is that checkboxes are not being checked due to the choices being array of objects. How can i resolve this issue? Although the same thing works for radio where there is only one selection.
Never mind i have found a solution here
checked binding does not properly compare primatives
Also it tells two ways for this. The Solution provided in fiddle is creepy so i will use the one using knockout version 3.0.0.
All i need to do is attach knockout-3.0.0.js instead of any other and then use checkedValue instead of value.
<input type="checkbox" name="choice[]"
data-bind="
checkedValue:ChoiceId,
checked:$root.AcceptedChoices"
>
And that's done. Hope it helps someone.
EDITS :
I noticed it is not working on the Chrome. So i found an alternative. I created these two functions.
self.ConvertToString = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(item.toString())
})
return AcceptedChoices
}
self.ConvertToInteger = function(accepted){
var AcceptedChoices = []
ko.utils.arrayForEach(accepted, function(item) {
AcceptedChoices.push(parseInt(item))
})
return AcceptedChoices
}
And use them
self.AcceptedChoices(self.ConvertToString(currentAnswer.AcceptedChoices))
To get the value
AcceptedChoices: self.ConvertToInteger(self.AcceptedChoices()),
You need to be checking to see if the Id of a choice is in the AcceptedChoices array. Use the ko.utils array function to help do that:
checked: function() { return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == ChoiceId();
} !== null }
You could put this into a function on your root object:
self.isChoiceAccepted = function(choiceId){
return ko.utils.arrayFirst($root.acceptedChoices(), function(item){
return item == choiceId;
} !== null
};
then call it in your data-bind as:
checked: function() { return $root.isChoiceAccepted(ChoiceId()); }
This isn't tested, I'm not 100% sure that the arrayFirst method returns null if it doesn't find a matching item in the array, so chack that.
I am new to Javascript (familiar with C/C++) and I am trying to parse out an XML file and store it in an Array of Objects. The structure is similar to a bullet list where there is one main List item and possibly multiple List subitems:
var MenuLine =
[{
label : "null",
icon : "null",
Subitem:
[{
label : "null",
icon : "null"
}]
}];
Which allows me to use the following syntax:
var someRandomSubitemText = MenuLine[2].Subitem[4].label;
I tried populating this array using the .push method:
var tempMenuLine = [];
var tempSubitem = [];
$(xml).find("item").each(function()
{
tempMenuLine.label = $(xml).children("label").text();
tempMenuLine.icon = $(xml).children("icon").text();
$(this).children("subitem").each(function()
{
tempSubitem.label = $(this).children("label").text();
tempSubitem.icon = $(this).children("icon").text();
tempMenuLine.Subitem.push(tempSubitem);
});
MenuLine.push(tempMenuLine);
});
However this does not work since the .push method passes a reference to tempMenuLine and I am overwriting tempMenuLine with each iteration. Is there a way that I could write directly to the MenuLine array using something similar to the following syntax?
$(xml).find("item").each(function(index1)
{
MenuLine[index1].label = $(xml).children("label").text();
MenuLine[index1].icon = $(xml).children("icon").text();
$(this).children("subitem").each(function(index2)
{
MenuLine[index1].Subitem[index2].label = $(this).children("label").text();
MenuLine[index1].Subitem[index2].icon = $(this).children("icon").text();
});
});
Move your temp var declarations inside of your loops:
$(xml).find("item").each(function() {
var tempMenuLine = [];
tempMenuLine[0].label = $(xml).children("label").text();
tempMenuLine[0].icon = $(xml).children("icon").text();
tempMenuLine[0].Subitem = []
$(this).children("subitem").each(function(){
var tempSubitem = [];
tempSubitem[0].label = $(this).children("label").text();
tempSubitem[0].icon = $(this).children("icon").text();
tempMenuLine[0].Subitem.push(tempSubitem);
});
MenuLine.push(tempMenuLine);
});
This way, you're initializing a new item for each iteration of the loops, removing the "link" it had to the previous item.
A recursive solution just for fun.
var MenuLine = Xml2Array(xmlText, 'item');
function Xml2Array(xmlDocument, itemName) {
if (!$(itemName, xmlDocument).length) {
return;
}
var tmpArray = [];
$(itemName, xmlDocument).each(function() {
tmpArray.push({
label: $('label', this).first().text(),
icon: $('icon', this).first().text(),
Subitem: Xml2Array(this, 'subitem')
});
});
return tmpArray;
}