This question already has answers here:
Split array of objects into new arrays based on year of object's date
(3 answers)
Closed 9 years ago.
I was wondering how I could turn this:
var data = [
{id:1,option1:'short',option2:'red',option3:'gold'},
{id:2,option1:'short',option2:'red',option3:'silver'},
{id:3,option1:'short',option2:'blue',option3:'gold'},
{id:4,option1:'short',option2:'blue',option3:'silver'},
{id:5,option1:'long',option2:'red',option3:'gold'},
{id:6,option1:'long',option2:'red',option3:'silver'},
{id:7,option1:'long',option2:'blue',option3:'gold'},
{id:8,option1:'long',option2:'blue',option3:'silver'}]
Into something formatted like this using Jquery.
var new_data = {
short:{
red:{gold:1,silver:2},
blue:{gold:3,silver:4}
},
long:{
red:{gold:5,silver:6},
blue:{gold:7,silver:8}
}
}
That is easier than you might think. Try this:
function helper(obj,tree,value) {
for( var i=0, l=tree.length; i<l-1; i++) {
obj[tree[i]] = obj[tree[i]] || {};
obj = obj[tree[i]];
}
obj[tree[i]] = value;
}
var new_data = {}, l = data.length, i;
for( i=0; i<l; i++) {
helper(new_data,[data[i].option1,data[i].option2,data[i].option3],data[i].id);
}
This plain JS will do it:
var data = […];
var new_data = {};
for (var i=0; i<data.length; i++) {
var o = new_data;
for (var j=1; j<3; j++) {
var prop = data[i]["option"+j];
o = o[prop] || (o[prop] = {});
}
o[data[i]["option"+j]] = data[i].id;
}
But it looks easier to use that nested schema in the first place?
You can use .reduce() like this:
var new_data = data.reduce(function(res, obj) {
if (!res[obj.option1])
res[obj.option1] = {};
if (!res[obj.option1][obj.option2])
res[obj.option1][obj.option2] = {};
res[obj.option1][obj.option2][obj.option3] = obj.id;
return res;
}, {});
or like this:
var new_data = data.reduce(function(res, obj) {
var o = res;
for (var i = 1; i < 3; i++)
o = (o[obj["option" + i]] = o[obj["option" + i]] || {});
o[obj.option3] = obj.id;
return res;
}, {});
Related
I've looked around for some help on this topic but was unable to find some help or guidance.
My problem is I am attempting to perform a sort on a series of values separated by an equals sign.
"Foo=Bar , Shenanigans=Fun, A=B ...etc"
My current sort works, but only if no value is the same. If I have some values like:
"Foo=Bar, A=Bar, Potato=Bar"
When the sort is complete they will all be "A=Bar"
My current sort looks like this, would someone be able to point me in the right direction?
$('#sortByValue').click(function() {
var textValueArray = document.getElementById('nameValuePairList');
textArray = new Array();
valueArray = new Array();
oldValues = new Array();
for (i = 0; i < textValueArray.length; i++) {
valueArray[i] = textValueArray.options[i].value;
textArray[i] = textValueArray.options[i].text;
oldValues[i] = textValueArray.options[i].value;
}
valueArray.sort(function(a, b) {
return a.toLowerCase().localeCompare(b.toLowerCase());
});
for (i = 0; i < textValueArray.length; i++) {
textValueArray.options[i].value = valueArray[i];
for (j = 0; j < textValueArray.length; j++) {
if (valueArray[i] == oldValues[j]) {
textValueArray.options[i].text = textArray[j];
j = textValueArray.length;
}
}
}
});
I know that my problem lies here: valueArray[i] == oldValues[j]
as when the data comes in valueArray = {Bar, Foo, Bar} while textArray = {Foo=Bar, A=Foo, Test=Bar}
However, I am unsure how to best resolve it.
Sort textArray directly, don't use valueArray since it will contain duplicates:
textArray.sort(function(a,b){
var aa = a.split('=')
var bb = b.split('=')
var a_key = aa[0].toLowerCase(), a_val = aa[1].toLowerCase();
var b_key = bb[0].toLowerCase(), b_val = bb[1].toLowerCase();
if (a_val == b_val) return a_key.localeCompare(b_key);
return a_val.localeCompare(b_val);
})
I would do something like this:
document.getElementById('sortByName').onclick = sortByName;
function sortByName(){
var myList = document.getElementById('list');
var values = [];
for (var i=0;i<myList.options.length;i++) {
values[i] = myList.options[i].text;
}
values.sort(function (a, b){
if(a !== "" && b !== ""){
return a.split('=')[0].localeCompare(b.split('=')[0]);
} else {
return 0;
}
});
clearList(myList);
fillList(myList, values);
}
function clearList(list) {
while (list.options.length > 0) {
list.options[0] = null;
}
}
function fillList(myList, values){
for (var i=0;i<values.length;i++) {
var option = document.createElement("option");
option.text = values[i];
myList.options[i] = option;
}
}
Take a look at this demo
The reasoning behind doing this at all will have you wondering why, in the future. I think you want something like this:
function inArray(v, a){
for(var i=0,l=a.length; i<l; i++){
if(a[i] === v){
return true;
}
}
return false;
}
function sortWeirdString(str){
var pairs = str.split(/\s?,\s?/), n = [], v = [], c = [], ci, idx = [], cl, nv = [], ra = [];
for(var i=0,l=pairs.length; i<l; i++){
var pair = pairs[i].split(/\s?=\s?/);
n.push(pair[0]); v.push(pair[1]);
}
c = n.concat().sort(); cl = c.length
for(var i=0; i<cl; i++){
var cv = c[i];
if(n.indexOf){
ci = n.indexOf(cv);
if(inArray(ci, idx)){
ci = n.indexOf(cv, ci+1);
}
idx.push(ci);
}
else{
for(var x=0; x<cl; x++){
if(n[x] === cv){
if(inArray(x, idx)){
continue;
}
idx.push(x);
}
}
}
}
for(var i=0,l=idx.length; i<l; i++){
ra.push(c[i]+'='+v[idx[i]]);
}
return ra.join(', ');
}
$('#sortByValue').click(function(){
console.log(sortWeirdString($('#nameValuePairList').val()));
}
Update 2019
The spec has changed and #Array.prototype.sort is now a stable sort.
The elements of this array are sorted. The sort must be stable (that
is, elements that compare equal must remain in their original order)
This is already implemented in V8
I have the following array
['.some_class &.green_mod','.some_class &.red_mod','another_class &.green_mod','another_class &.orange_mod']
I want to get this array from it:
['.some_class &.green_mod &.red_mod','another_class &.green_mod &.orange_mod']
Is it possible?
you can try:
var preArr = ['.some_class &.green_mod', '.some_class &.red_mod', 'another_class &.green_mod', 'another_class &.orange_mod'];
var newArr = [];
preArr.forEach(function (item) {
var has = false;
var preWords = item.split('&');
for (var i = 0; i < newArr.length; ++i) {
var newWords = newArr[i].split('&');
if (newWords[0] == preWords[0]) {
has = true;
for (var j = 0; j < preWords.length; ++j) {
if (newWords.indexOf(preWords[j]) < 0) {
newWords.push(preWords[j]);
}
}
newArr[i] = newWords.join('&');
}
}
if (!has) {
newArr.push(item);
}
});
console.log(newArr);
demo
var test = (function() {
var fmap1 = function(e) { return e.trim().split(/\s+/); };
var fmap2 = function(e) { return e.join(" "); };
var fsort = function(e1,e2) { return e1[0] == e2[0] ? 0 : e1[0] > e2[0] ? 1 : -1; };
return function test(a) {
var a1 = a.map(fmap1).sort(fsort);
var s, a2 = [];
for (var i = 0, l = a1.length; i < l; i++) {
if (s != a1[i][0]) {
s = a1[i][0];
a2.push([s]);
}
a1.push.apply(a2[a2.length - 1], a1[i].slice(1));
}
return a2.map(fmap2);
};
})();
var arr = [' .some_class &.green_mod',' another_class &.green_mod','.some_class &.red_mod','another_class &.orange_mod'];
console.log(test(arr));
This seems to work for me:
var arr = ['.some_class &.green_mod','.some_class &.red_mod','another_class &.green_mod','another_class &.orange_mod'];
var obj = {};
var finalArr = [];
for(var i=0,c=arr.length;i<c;i++)
{
var parts = arr[i].split(' ');
var key = parts[0];
if(!obj[key]) obj[key] = [];
obj[key].push(parts.slice(1).join(' '));
}
var keys = Object.keys(obj);
for(var i=0,c=keys.length;i<c;i++)
{
var key = keys[i];
finalArr.push(key+' '+obj[key].join(' '))
}
console.log(finalArr);
Basically just loop through each one, and use the first word as a key to an array of strings to be appended, then loop through the object and join the keys with all of their array elements.
N.B. References to key are the first word
Solution bellow for data like key+separator+value where you are sure that there is no duplication in keys or You do not care if values are duplicated
// maping function that treats part before separator as key and saves incremetaly
// all values under that key (duplicated vales are possible)
// only one separator per input entry is allowed
function mapFun (el, obj, separator) {
var e = el.split(separator);
var key = e[0];
var val = e[1];
obj[key] = obj[key] ? obj[key] + separator + val : separator + val;
}
function combineClasses (arr) {
var result = [];
var separator = ' &';
var obj = Object.create(null); // create empty object without any properties or inheritance chain
for (var i=0; i<arr.length; i++) {
mapFun(arr[i], obj, separator);
}
for (var p in obj) {
result.push (p + obj[p]);
}
return result;
}
combineClasses(arr);
Bellow solution will work for data like before and data like key+separator+value1+separator+value2 and will not allow for duplicated values for the same key
function mapFun (el, separator, obj) {
var parts = el.split(separator);
var key = parts[0];
if (!obj[key]) {
obj[key] = Object.create(null);
}
for (var i=1; i<parts.length; i++) {
obj[key][parts[i]] = null;
}
}
function combineClasses (arr) {
var result = [];
var separator = ' &';
var obj = Object.create(null); // create empty object without any properties
for (var i=0; i<arr.length; i++) {
mapFun(arr[i], separator, obj);
}
for (var p in obj) {
var values = Object.keys(obj[p]).join(separator);
result.push (p + separator + values);
}
return result;
}
combineClasses(arr);
arr is where Your data comes in
var arr = ['.some_class &.green_mod','.some_class &.red_mod','another_class &.green_mod','another_class &.orange_mod']
I'm looping through a set of inputs. I need to tally up the grouped totals.
var compoundedArray = new Array();
holder.find(".dataset input").each(function(index) {
var val = $(this).val();
var dataType = $(this).data("type");
var localObj = {};
localObj[dataType] = val;
compoundedArray.push(localObj);
});
I have an object like this
[
{
"growth":30
},
{
"growth": 40
},
{
"other": 20
}
]
how do I loop through the object to produce something like
[
{
"growth": 70
},
{
"other": 20
}
]
if I looped over the initial array object
for (var i = 0; i < compoundedArray.length; i++) {
console.log(compoundedArray[i]);
}
how would I go about checking to ensure I don't have duplicates - and that I can tally up the results?
I think your selection of data structure is a bit too complicated. Try something like.
var compoundedObject = {};
holder.find(".dataset input").each(function(index) {
var val = $(this).val();
var dataType = $(this).data("type");
//Assuming all values are integers and can be summed:
if( compoundedObject.hasOwnProperty(dataType) )
{
compoundedObject[dataType] += val;
}
else
{
compoundedObject[dataType] = val;
}
});
You will end up with an object, not an array though.
var add=function (a,b){ a=a||0; b=b||0; return a+b};
var input=[ {growth:30},{growth:40},{other:20} ],output=[],temp={};
$.each(input,function(i,o){
var n;
for(i in o)
{n=i;break}
temp[n]=add(temp[n],o[n]);
});
$.each(temp,function(i,o){
var k={};
k[i]=o;
output.push(k)
});
find output at output variable.
Do not post much specific question, It might not help others.
This works. And it's pure javascript.
var totals = {};
for (var i = 0; i < compoundedArray.length; i++) {
var item = compoundedArray[i];
for (var key in item) {
totals[key] = (totals[key] || 0) + item[key]
}
};
You can loop trough an Object with a for loop.
If you want to delete an item simply set it to null.
Example:
for(var i in compoundedArray){
for(var j in compoundedArray){
if(i == j){
compoundedArray[i] += compoundedArray[j];
compoundedArray[j] = null;
}
}
}
You can do the following:
var totals = [], tmp = {};
for (var i = 0; i < compoundedArray.length; i++) {
var obj = compoundedArray[i];
for (var j in obj) {
tmp[j] = tmp[j] || 0;
tmp[j] += obj[j];
}
}
for(var k in tmp) {
var obj = {};
obj[k] = tmp[k];
totals.push(obj);
}
See this working demo
obj = {'a':['hello', 'hie'], 'b':['World', 'India']}
To
array = [{'a':'hello','b':'World'}, {'a':'hie','b':'India'}]
Best way to convert this or any build-in method for this conversion using JQuery.
Try this Code,
obj = {'a':['hello', 'hie'], 'b':['World', 'India']}
var key = Object.keys(obj);
array = [];
for (var i = 0; i < obj['a'].length; i++){
o = {}
for (k in key){
o[key[k]] = obj[key[k]][i]
}
array.push(o)
}
var obj = {'a':['hello', 'hie'], 'b':['World', 'India']};
var array = [];
for (var prop in obj)
for (var i=0; i<obj[prop].length; i++) {
var o = array[i] || (array[i] = {});
o[prop] = obj[prop][i];
}
No jQuery needed. With jQuery, it might look like this:
var array = [];
$.each(obj, function(prop) {
$.each(this, function(i) {
var o = array[i] || (array[i] = {});
o[prop] = this;
});
});
slower and less readable. Do not use.
Not sure if this can even be done, but I'll ask anyway:
Suppose if I have an array of names:
['bob', 'sue', 'dan']
And I want to dynamically create an object from those names:
bob.sue.dan = 5;
Is it possible?
Here you go, will preserve existing objects:
var namespace = function(name, separator, container){
var ns = name.split(separator || '.'),
o = container || window,
i,
len;
for(i = 0, len = ns.length; i < len; i++){
o = o[ns[i]] = o[ns[i]] || {};
}
return o;
};
e.g. usage:
namespace("com.example.namespace");
com.example.namespace.test = function(){
alert("In namespaced function.");
};
or for your example using an array.
var ns = ['bob', 'sue', 'dan'];
namespace(ns.join('.'));
bob.sue.dan.foobar = true;
or extending an existing object:
var bob = {}
namespace("foo.bar",".",bob);
bob.foo.bar = true;
Edit: updated as requested:
var namespace = function(name, separator, container, val){
var ns = name.split(separator || '.'),
o = container || window, i, len;
for(i = 0, len = ns.length; i < len; i++){
var v = (i==len-1 && val) ? val : {};
o = o[ns[i]] = o[ns[i]] || v;
}
return o;
};
namespace("bob.sue.dan",null,null,5);
alert(bob.sue.dan);
See working example: http://jsfiddle.net/herostwist/hu6j9/
Then you can do:
function makeOjectTree(propNames) {
var name;
var o = {};
var result = o;
for (var i=0, iLen=propNames.length; i<iLen; i++) {
name = propNames[i];
if (!o[name]) {
o[name] = {};
o = o[name];
}
}
return result;
}
var names = ['bob', 'sue', 'dan'];
var objs = [];
for(var i=0; i<names.length; i++) {
objs.push(names[i]);
var val = (i==names.length-1) ? "5" : "{}";
eval(objs.join(".") + " = " + val);
}
alert(bob.sue.dan);
Demo: http://jsfiddle.net/EpZm2/1/
sure you can ...
var obj = 5;
var yourarray = ['bob', 'sue', 'dan'];
yourarray = yourarray.reverse();
for(e in yourarray) {
var tmpobj = obj;
obj = new Object();
obj[yourarray[e]] = tmpobj;
// if you already have an object
if (e+1 == yourarray.length) {
your_current_existing_object[yourarray[e]] = tmpobj;
}
}
Yes this is possible.
You can define new properties on an object this way:
var obj = {};
obj["bob"] = {};
obj["bob"]["sue"] = {};
obj["bob"]["sue"]["dan"] = 5;
So you can also do it with an array of property names ;)