I want to create a function in Javascript which takes an array as argument and returns a list of objects. I have an array like this:
var arr= [10,20,30];
console.log(listFunction(arr));
The result should look like this:
{'val':10, 'restList':{'val':20, 'restList':{'val':30,'restList':'null'}}}
I have tried the forEach() function:
function listFunction(parentArr) {
var listOfObjects = [];
parentArr.forEach(function (entry, thisArg) {
var singleObj = {}
singleObj['val'] = entry;
singleObj['restList'] = singleObj;
listOfObjects[thisArg] = singleObj;
});
return listOfObjects;
};
You need to use a recursive function:
function listFunction(arr){
if(arr.length == 0){
return null;
}else{
return {val: arr[0], restList: listFunction(arr.slice(1,arr.length))};
}
}
This is the Lisp-style recursive list algorithm.
var recursiveList = function (array) {
return recursiveListHelper(arr, {});
};
var recursiveListHelper = function (array, obj) {
if (array.length === 0) //stopping condition
return null;
var car = array[0]; //current element
var cdr = array.slice(1); //rest of list
obj = {val: car};
obj.restList = recursiveListHelper(cdr, obj);
return obj;
};
You mentioned that you wanted to avoid using Array.slice. This solution uses array indexing instead of splitting into subarrays.
var recursiveIndexed = function (array) {
return recursiveIndexedHelper(array, {}, 0);
};
var recursiveIndexedHelper = function (array, obj, index) {
if (index == array.length)
return null;
var car = array[index];
obj = {val: car };
obj.restList = recursiveIndexedHelper(array, obj, index + 1);
return obj;
};
A plunker as example.
Related
Given the following string with key-value pairs, how would you write a generic function to map it to an object?
At the moment, I am just splitting by : and ; to get the relevant data, but it doesn't seem like a clean approach.
This my code at the moment:
var pd = `id:S76519;sku:S76519;name:StarGazer 3000;model:ICC74`;
var tempPd = pd.split(';');
for (i = 1; i < tempPd.length; i++) {
var b = tempPd[i].split(':');
console.log(b[1]);
}
What about using reduce:
function objectify(str) {
return str.split(";").reduce(function (obj, item) {
var a = item.split(":");
obj[a[0]] = a[1];
return obj;
}, {});
}
var strObj = "id:S76519;sku:S76519;name:StarGazer 3000;model:ICC74";
console.log(objectify(strObj));
or:
function objectify(str){
return str.split(";").reduce((obj,item)=>{
var a = item.split(":");
obj[a[0]]=a[1];
return obj;
},{});
}
var strObj = "id:S76519;sku:S76519;name:StarGazer 3000;model:ICC74";
console.log(objectify(strObj));
I implemented an aggregation function but the only problem I have now is that I lost my key: value format e.g [{name:"Apples",val:8},{name:"Banana",val: 9}].
function agrregate(a){
var targetObj = {};
var result;
var b = JSON.parse(JSON.stringify(a));
var trees= b.length;
if(!trees){
trees = 0
}
for (var i = 0; i < trees; i++) {
if (!targetObj.hasOwnProperty(b[i].key)) {
targetObj[b[i].key] = 0;
}
targetObj[b[i].key] += b[i].val;
}
result = JSON.stringify(targetObj);
return result;
}
This is the result i get when agrregate function completes.
{"Apple":8,"Banana":9}
Instead of
{name:"Apple", val:8}, {name:"Banana", val:9}
Use a reducer to aggregate. You don't need to do stuff with JSON stringify/parse.
To get back to an array of objects, you use map and Object.keys
var test = [{name:"Apples",val:5},{name:"Banana",val: 9},{name:"Apples",val:3}]
var aggregate = function(arr) {
return arr.reduce(function(result, obj) { // Create one object (result)
result[obj.name] = (result[obj.name] || 0) + obj.val; // Add a new key/or increase
return result // Return the object
}, {});
};
var wrap = function(obj) {
return Object.keys(obj) // Create an array of keys
.map(function(key) {
return { // Specify the format
name: key,
val: obj[key]
};
});
};
console.log(aggregate(test));
console.log(wrap(aggregate(test)));
I have one object that I had to take apart into two arrays to handle properly.
It looked like this:
{
city:"stuttgart",
street:"randomstreet",
...
}
Since it needs to fit a certain directive I had to convert it to:
[
{key:"city", value:"stuttgart"}
{key:"street", value:"randomstreet"},
...
]
for this I first used
var mapFromObjectWithIndex = function (array) {
return $.map(array, function(value, index) {
return [value];
});
};
var mapFromObjectWithValue = function (array) {
return $.map(array, function(value, index) {
return [index];
});
});
to create two arrays, one containing the old key, the other one is holding the old value. Then I created another, two dimensional array map them into a single array doing this
var mapToArray = function (arrayValue, arrayIndex) {
var tableData = [];
for (var i = 0; i<arrayIndex.length; i++){
tableData[i] = {key:arrayIndex[i] , value:arrayValue[i]};
}
return tableData;
};
(maybe I have already messed up by here, can this be done any easier?)
Now, I use the array (tableData) to display the data in a form. The value fields can be edited. In the end, I want to convert the array (tableData) to its original. (see first object)
Please note, that the original object doesn't only contain strings as values, but can also contain objects as well.
I think conversion can be definitely easier:
var obj = {
city:"stuttgart",
street:"randomstreet",
};
var tableData = Object.keys(obj).map(k => {return {key: k, value: obj[k]}});
console.log(tableData);
var dataBack = {};
tableData.forEach(o => dataBack[o.key] = o.value);
console.log(dataBack);
What do you want to do with objects? Do you want to expand them as well? If yes you can do something like this (and it works with nested objects as well):
var obj = {
city:"stuttgart",
street:"randomstreet",
obj: {a: 'a', b: 'b'},
subObject: {aha: {z: 'z', y: 'y'}}
};
function trasformToTableData(obj) {
if (typeof obj !== 'object') return obj;
return Object.keys(obj).map(k => {return {key: k, value: trasformToTableData(obj[k])}});
}
var tableData = trasformToTableData(obj);
console.log(tableData);
function transformBack(obj) {
if (Array.isArray(obj)) {
var support ={};
for (let i = 0; i < obj.length; i++) {
support[obj[i].key] = transformBack(obj[i].value)
}
return support;
}
return obj;
}
var dataBack = {};
tableData.forEach(o => dataBack[o.key] = transformBack(o.value));
console.log(dataBack);
Let's have some fun and turn our object into iterable to do the job as follows;
var input = {city:"stuttgart", street:"randomstreet", number: "42"};
output = [];
input[Symbol.iterator] = function*(){
var ok = Object.keys(this),
i = 0;
while (i < ok.length) yield {key : ok[i], value: this[ok[i++]]};
};
output = [...input];
console.log(output);
This function will map your object to an array when you call objVar.mapToArray(), by using Object.keys() and .map()
Object.prototype.mapToArray = function() {
return Object.keys(this).map(function(v) {
return { key: v, value: this[v] };
}.bind(this));
}
I would do something like this:
var dataObj = {
city:"stuttgart",
street:"randomstreet",
};
function toKeyValue(obj) {
var arr = [];
for (var key in obj) {
if(obj.hasOwnProperty(key)) {
arr.push({'key': key, 'value': obj[key]});
}
}
return arr;
}
var arrayKeyValue = toKeyValue(dataObj);
console.log(arrayKeyValue);
var Array = [{"Name":"Temp","Date":"2014-10-23"},
{"Name":"Temp","Date":"2014-10-22"},
{"Name":"Temp","Date":"2014-10-18"},
{"Name":"Temp","Date":"2014-10-19"},
{"Name":"Temp2","Date":"2014-10-12"},
{"Name":"Temp2","Date":"2014-06-12"}]
What would be the best way to filter the above array on the following condition.
*If the name is the same, then filter away all the objects with the same name and leave only the object with the latest date left.
All I can think of is to do for loops. Note that Date is a real dateObject and not a string as I have wrote above which means that you can do Date comparisons.
Using Array.reduce(), i was able to get it down to an object containing only Temp and Temp2 with their dates:
var obj = array.reduce(function(base,cur){
if (base[cur.Name]) {
if (base[cur.Name].Date < cur.Date) {
base[cur.Name].Date = cur.Date;
} else {
return base;
}
} else {
base[cur.Name] = cur;
return base;
}
},{});
From there, you can just get the object's values with Object.keys().map():
array = Object.keys(obj).map(function(k){
return obj[k];
});
var array = [{"Name":"Temp","Date":new Date("2014-10-23")},
{"Name":"Temp","Date":new Date("2014-10-22")},
{"Name":"Temp","Date":new Date("2014-10-18")},
{"Name":"Temp","Date":new Date("2014-10-19")},
{"Name":"Temp2","Date":new Date("2014-10-12")},
{"Name":"Temp2","Date":new Date("2014-06-12")}];
var obj = {};
for(var i in array){
if(!obj[array[i].Name] || obj[array[i].Name].getTime() < array[i].Date.getTime())
obj[array[i].Name] = array[i].Date;
}
then if you need it to be an array of objects:
array = [];
for(var name in obj){
array.push({Name: name, Date: obj[name]});
}
var Array = [{"Name":"Temp","Date":"2014-10-23"},
{"Name":"Temp","Date":"2014-10-22"},
{"Name":"Temp","Date":"2014-10-18"},
{"Name":"Temp","Date":"2014-10-19"},
{"Name":"Temp2","Date":"2014-10-12"},
{"Name":"Temp2","Date":"2014-06-12"}]
var result = {};
Array.forEach(function(item) {
var name = item['Name'];
if(result[name]) {
result[name].push(item['Date']);
} else {
result[name] = [item['Date']];
}
})
Object.keys(result).forEach(function(item) {
result[item] = Math.max.apply(null, result[item]);
})
aggragate the array by name to {Name: 'Temp', Date: ['2014-10-23', '2013-10-32', 'xxxx']}
then get the max date by Math.max.apply
Here you go, Works perfect for any random orders
var Array = [{"Name":"Temp","Date":"2014-10-03"},
{"Name":"Temp","Date":"2014-10-22"},
{"Name":"Temp","Date":"2014-10-18"},
{"Name":"Temp","Date":"2014-10-19"},
{"Name":"Temp2","Date":"2014-10-12"},
{"Name":"Temp2","Date":"2014-06-12"}];
var tempArray = []; //This array will hold your Result Set
var tempArrayName = [];
var tempDate = {};
$.each(Array, function(i, v) {
if(tempArrayName.indexOf(Array[i].Name) < 0){
tempArray.push({ "Name":Array[i].Name,"Date":Array[i].Date});
tempArrayName.push(Array[i].Name);
tempDate[Array[i].Name] = Array[i].Date;
}else{
if( new Date((Array[i].Date))> new Date(tempDate[Array[i].Name])){
$.each(tempArray, function(j, k) {
if(tempArray[j].Name == Array[i].Name){
tempArray[j].Date = Array[i].Date;
}
});
}
}
});
console.log(tempArray);
I'd advise against overwriting the Array object. Anyhow,
one way would be to group the dates using an object:
var data = [
{"Name":"Temp","Date": new Date("2014-10-23")},
{"Name":"Temp","Date": new Date("2014-10-22")},
{"Name":"Temp","Date": new Date("2014-10-18")},
{"Name":"Temp","Date": new Date("2014-10-19")},
{"Name":"Temp2","Date": new Date("2014-10-12")},
{"Name":"Temp2","Date": new Date("2014-06-12")}
];
var name2dates = Object.create(null);
data.forEach(function(thing){
if(!name2dates[thing.Name]){
name2dates[thing.Name] = thing.Date;
}else{
name2dates[thing.Name] = thing.Date > name2dates[thing.Name] ?
thing.Date : name2dates[thing.Name];
}
});
And to create a 'filtered' array from this, if that's what you
need, then:
Object.keys(name2dates).map(function(name){
return {Name: name, Date: name2dates[name]};
});
Is this helpful?
$.grep(Array, function(v) {
return v.Name === "Temp";
}).reduce(function (a, b) { return a.Date > b.Date ? a : b; });
I have this array:
["userconfig", "general", "name"]
and I would like it to look like this
data_structure["userconfig"]["general"]["name"]
I have tried this function:
inputID = "userconfig-general-name"
function GetDataByID(inputID){
var position = '';
for (var i = 0; i < inputID.length; i++) {
var hirarchy = inputID[i].split('-');
for (var index = 0; index < hirarchy.length; index++) {
position += '["'+ hirarchy[index] +'"]';
}
}
return data_structure[position];
}
while hirarchy is the array. I get the [position] as a string which is not working well.
how can I make a js function which builds the object path dynamically by an array?
var arr = ["userconfig", "general", "name"];
var dataStructure = arr.reduceRight(function (value, key) {
var obj = {};
obj[key] = value;
return obj;
}, 'myVal');
Ends up as:
{ userconfig : { general : { name : 'myVal' } } }
Note that you may need a polyfill for the reduceRight method: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/ReduceRight
The below function will take an object to modify and an array filled with the properties needed:
function objPath(obj,path){
path.forEach(function(item){
obj[item] = {};
obj = obj[item];
});
}
var myobj = {};
objPath(myobj,["test","test2","test3"]);
console.log(myobj);
//outputs
Object {test: Object}
test: Object
test2: Object
test3: Object
The function loops over the array creating the new object property as a new object. It then puts a reference to the new object into obj so that the next property on the new object can be made.
JSFiddle
Recursive function
var array = ["userconfig", "general", "name"];
function toAssociative(array) {
var index = array.shift();
var next = null;
if (array.length > 0) {
next = toAssociative(array);
}
var result = new Array();
result[index] = next;
return result;
}