Pass object data to upper object property - javascript

I have a array list which is bellow and want to assign a value from bottom to up.
Code sample:
var tempArrayList=[
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:null,org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:"Office",org3:null,org4:null,org5:"Pay"},
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:"head1",org2:null,org3:null,org4:"Payroll",org5:"Pay"}
];
for(var i=0;i<tempArrayList.length;i++) {
var temp=tempArrayList[i];
var org1=temp.org1;
var org2=temp.org2;
var org3=temp.org3;
var org4=temp.org4;
var org5=temp.org5;
document.write(JSON.stringify(temp));
document.writeln("<br>");
}
My Questions: how can I set the org2 value to org1, org3 value to org2......
// if org1 is null then temp.org1 = temp.org2, temp.org2 = temp.org3, temp.org3 = temp.org4, temp.org4 = temp.org5
// if org1 and org2 is null then temp.org1 = temp.org3, temp.org2 = temp.org4, temp.org3 = temp.org5, temp.org4 = null

Filter all the null out of values array then loop over keys and assign filtered value or null
var tempArrayList=[
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:null,org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:"Office",org3:null,org4:null,org5:"Pay"},
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:"head1",org2:null,org3:null,org4:"Payroll",org5:"Pay"}
];
tempArrayList.forEach((o) => {
const vals = Object.values(o).filter(v => v);
Object.keys(o).forEach((key, i) => o[key] = vals[i] || null);
});
console.log(tempArrayList)
.as-console-wrapper {max-height:100%!important;}

Accepted answer fails on falsy values ({org1:false}), mutates original object and falsely assumes Object.keys returns in a particular order.
There is no guarantee in the order Object.keys returns the array of keys in the right order:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
in the same order as that provided by a for...in loop
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
A for...in loop iterates over the properties of an object in an arbitrary order
If the code with Object.keys works then try it again but add this first:
tempArrayList = tempArrayList.map(
x =>
Object.keys(x).reverse().reduce(
(acc,key) => {acc[key]=x[key];return acc;}
,{}
)
);
Here is one solution to the problem that doesn't rely on Object.keys order, does not mutate and does not fail on falsy member values:
const collapse = function(direction,reason,subject,seed,keys){
if(Array.isArray(subject)){
keys = (function(x){
const ret = [];
while(++x<subject.length){
ret.push(x);
}
return ret;
}(-1));
}
keys =
(direction>0)
? keys
: keys.reverse();
const sortedKeys = keys.filter(
function(key){return !reason(subject[key])}
).concat(
keys.filter(
function(key){return reason(subject[key])}
)
);
return keys.reduce(
function(acc,key,index) {
acc[key] = subject[sortedKeys[index]]
return acc;
}
,seed
);
}
const collapseLeft = function(reason,keys){
return function(seed,subject){
return collapse(1,reason,subject,seed,keys);
}
};
const collapseRight = function(reason,keys){
return function(seed,subject){
return collapse(-1,reason,subject,seed,keys);
}
};
const tempArrayList=[
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:null,org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:"Office",org3:null,org4:null,org5:"Pay"},
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:"head1",org2:null,org3:"Pay",org4:null,org5:null}
];
const isNull = function(x){return x===null;};
console.log(
tempArrayList.map(
function(item){
//seed can be an object ({}) or array ([]) see other log
return collapseLeft(
isNull,
["org1", "org2", "org3", "org4", "org5"]
)
({},item);
}
)
);
//collapse right example
console.log(
JSON.stringify(
collapseRight(
isNull,
["org1", "org2", "org3", "org4", "org5"]
)({},{org1:"head1",org2:null,org3:"Pay",org4:null,org5:null})
,undefined
,2
)
);
//array example (seed is [])
console.log(
JSON.stringify(
collapseRight(isNull)([],[1,2,3,null,4,null,5])
,undefined
,2
)
);

Is this what you're looking for?
This code loops through the object keys, moving the values up and setting the last one to null.
var tempArrayList=[
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:null,org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:null,org2:"Office",org3:null,org4:null,org5:"Pay"},
{org1:null,org2:"Office",org3:"HR",org4:"Payroll",org5:"Pay"},
{org1:"head1",org2:null,org3:null,org4:"Payroll",org5:"Pay"}
];
for (var i = 0; i < tempArrayList.length; i++) {
var keys = Object.keys (tempArrayList[i]);
for (var j = 0; j < keys.length; j++) {
if (j < keys.length - 1) {
tempArrayList[i][keys[j]] = tempArrayList[i][keys[j + 1]];
} else {
tempArrayList[i][keys[j]] = null;
}
}
}

Related

Js how to set an item at a nested index in an empty array [duplicate]

I hope someone can help me with this Javascript.
I have an Object called "Settings" and I would like to write a function that adds new settings to that object.
The new setting's name and value are provided as strings. The string giving the setting's name is then split by the underscores into an array. The new setting should get added to the existing "Settings" object by creating new nested objects with the names given by each part of the array, except the last part which should be a string giving the setting's value. I should then be able to refer to the setting and e.g. alert its value. I can do this in a static way like this...
var Settings = {};
var newSettingName = "Modules_Video_Plugin";
var newSettingValue = "JWPlayer";
var newSettingNameArray = newSettingName.split("_");
Settings[newSettingNameArray[0]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]] = {};
Settings[newSettingNameArray[0]][newSettingNameArray[1]][newSettingNameArray[2]] = newSettingValue;
alert(Settings.Modules.Mediaplayers.Video.Plugin);
... the part that creates the nested objects is doing this ...
Settings["Modules"] = {};
Settings["Modules"]["Video"] = {};
Settings["Modules"]["Video"]["Plugin"] = "JWPlayer";
However, as the number of parts that make up the setting name can vary, e.g. a newSettingName could be "Modules_Floorplan_Image_Src", I'd like to do this dynamically using a function such as...
createSetting (newSettingNameArray, newSettingValue);
function createSetting(setting, value) {
// code to create new setting goes here
}
Can anyone help me work out how to do this dynamically?
I presume there has to be a for...loop in there to itterate through the array, but I haven't been able to work out a way to create the nested objects.
If you've got this far thanks very much for taking the time to read even if you can't help.
Put in a function, short and fast (no recursion).
var createNestedObject = function( base, names ) {
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
};
// Usage:
createNestedObject( window, ["shapes", "triangle", "points"] );
// Now window.shapes.triangle.points is an empty object, ready to be used.
It skips already existing parts of the hierarchy. Useful if you are not sure whether the hierarchy was already created.
Or:
A fancier version where you can directly assign the value to the last object in the hierarchy, and you can chain function calls because it returns the last object.
// Function: createNestedObject( base, names[, value] )
// base: the object on which to create the hierarchy
// names: an array of strings contaning the names of the objects
// value (optional): if given, will be the last object in the hierarchy
// Returns: the last object in the hierarchy
var createNestedObject = function( base, names, value ) {
// If a value is given, remove the last name and keep it for later:
var lastName = arguments.length === 3 ? names.pop() : false;
// Walk the hierarchy, creating new objects where needed.
// If the lastName was removed, then the last object is not set yet:
for( var i = 0; i < names.length; i++ ) {
base = base[ names[i] ] = base[ names[i] ] || {};
}
// If a value was given, set it to the last name:
if( lastName ) base = base[ lastName ] = value;
// Return the last object in the hierarchy:
return base;
};
// Usages:
createNestedObject( window, ["shapes", "circle"] );
// Now window.shapes.circle is an empty object, ready to be used.
var obj = {}; // Works with any object other that window too
createNestedObject( obj, ["shapes", "rectangle", "width"], 300 );
// Now we have: obj.shapes.rectangle.width === 300
createNestedObject( obj, "shapes.rectangle.height".split('.'), 400 );
// Now we have: obj.shapes.rectangle.height === 400
Note: if your hierarchy needs to be built from values other that standard objects (ie. not {}), see also TimDog's answer below.
Edit: uses regular loops instead of for...in loops. It's safer in cases where a library modifies the Array prototype.
function assign(obj, keyPath, value) {
lastKeyIndex = keyPath.length-1;
for (var i = 0; i < lastKeyIndex; ++ i) {
key = keyPath[i];
if (!(key in obj)){
obj[key] = {}
}
obj = obj[key];
}
obj[keyPath[lastKeyIndex]] = value;
}
Usage:
var settings = {};
assign(settings, ['Modules', 'Video', 'Plugin'], 'JWPlayer');
My ES2015 solution. Keeps existing values.
const set = (obj, path, val) => {
const keys = path.split('.');
const lastKey = keys.pop();
const lastObj = keys.reduce((obj, key) =>
obj[key] = obj[key] || {},
obj);
lastObj[lastKey] = val;
};
Example:
const obj = {'a': {'prop': {'that': 'exists'}}};
set(obj, 'a.very.deep.prop', 'value');
console.log(JSON.stringify(obj));
// {"a":{"prop":{"that":"exists"},"very":{"deep":{"prop":"value"}}}}
Using ES6 is shorten. Set your path into an array.
first, you have to reverse the array, to start filling the object.
let obj = ['a','b','c'] // {a:{b:{c:{}}}
obj.reverse();
const nestedObject = obj.reduce((prev, current) => (
{[current]:{...prev}}
), {});
Another recursive solution:
var nest = function(obj, keys, v) {
if (keys.length === 1) {
obj[keys[0]] = v;
} else {
var key = keys.shift();
obj[key] = nest(typeof obj[key] === 'undefined' ? {} : obj[key], keys, v);
}
return obj;
};
Example usage:
var dog = {bark: {sound: 'bark!'}};
nest(dog, ['bark', 'loudness'], 66);
nest(dog, ['woff', 'sound'], 'woff!');
console.log(dog); // {bark: {loudness: 66, sound: "bark!"}, woff: {sound: "woff!"}}
I love this ES6 immutable way to set certain value on nested field:
const setValueToField = (fields, value) => {
const reducer = (acc, item, index, arr) => ({ [item]: index + 1 < arr.length ? acc : value });
return fields.reduceRight(reducer, {});
};
And then use it with creating your target object.
const targetObject = setValueToField(['one', 'two', 'three'], 'nice');
console.log(targetObject); // Output: { one: { two: { three: 'nice' } } }
Lodash has a _.set method to achieve this
let obj = {}
_.set(obj, ['a', 'b', 'c', 'd'], 'e')
or
_.set(obj, 'a.b.c.d', 'e')
// which generate the following object
{
"a": {
"b": {
"c": {
"d": "e"
}
}
}
}
Here is a simple tweak to jlgrall's answer that allows setting distinct values on each element in the nested hierarchy:
var createNestedObject = function( base, names, values ) {
for( var i in names ) base = base[ names[i] ] = base[ names[i] ] || (values[i] || {});
};
Hope it helps.
Here is a functional solution to dynamically create nested objects.
const nest = (path, obj) => {
const reversedPath = path.split('.').reverse();
const iter = ([head, ...tail], obj) => {
if (!head) {
return obj;
}
const newObj = {[head]: {...obj}};
return iter(tail, newObj);
}
return iter(reversedPath, obj);
}
Example:
const data = {prop: 'someData'};
const path = 'a.deep.path';
const result = nest(path, data);
console.log(JSON.stringify(result));
// {"a":{"deep":{"path":{"prop":"someData"}}}}
Inspired by ImmutableJS setIn method which will never mutate the original. This works with mixed array and object nested values.
function setIn(obj = {}, [prop, ...rest], value) {
const newObj = Array.isArray(obj) ? [...obj] : {...obj};
newObj[prop] = rest.length ? setIn(obj[prop], rest, value) : value;
return newObj;
}
var obj = {
a: {
b: {
c: [
{d: 5}
]
}
}
};
const newObj = setIn(obj, ["a", "b", "c", 0, "x"], "new");
//obj === {a: {b: {c: [{d: 5}]}}}
//newObj === {a: {b: {c: [{d: 5, x: "new"}]}}}
Appreciate that this question is mega old! But after coming across a need to do something like this in node, I made a module and published it to npm.
Nestob
var nestob = require('nestob');
//Create a new nestable object - instead of the standard js object ({})
var newNested = new nestob.Nestable();
//Set nested object properties without having to create the objects first!
newNested.setNested('biscuits.oblong.marmaduke', 'cheese');
newNested.setNested(['orange', 'tartan', 'pipedream'], { poppers: 'astray', numbers: [123,456,789]});
console.log(newNested, newNested.orange.tartan.pipedream);
//{ biscuits: { oblong: { marmaduke: 'cheese' } },
orange: { tartan: { pipedream: [Object] } } } { poppers: 'astray', numbers: [ 123, 456, 789 ] }
//Get nested object properties without having to worry about whether the objects exist
//Pass in a default value to be returned if desired
console.log(newNested.getNested('generic.yoghurt.asguard', 'autodrome'));
//autodrome
//You can also pass in an array containing the object keys
console.log(newNested.getNested(['chosp', 'umbridge', 'dollar'], 'symbols'));
//symbols
//You can also use nestob to modify objects not created using nestob
var normalObj = {};
nestob.setNested(normalObj, 'running.out.of', 'words');
console.log(normalObj);
//{ running: { out: { of: 'words' } } }
console.log(nestob.getNested(normalObj, 'random.things', 'indigo'));
//indigo
console.log(nestob.getNested(normalObj, 'improbable.apricots'));
//false
Inside your loop you can use lodash.set and will create the path for you:
...
const set = require('lodash.set');
const p = {};
const [type, lang, name] = f.split('.');
set(p, [lang, type, name], '');
console.log(p);
// { lang: { 'type': { 'name': '' }}}
try using recursive function:
function createSetting(setting, value, index) {
if (typeof index !== 'number') {
index = 0;
}
if (index+1 == setting.length ) {
settings[setting[index]] = value;
}
else {
settings[setting[index]] = {};
createSetting(setting, value, ++index);
}
}
I think, this is shorter:
Settings = {};
newSettingName = "Modules_Floorplan_Image_Src";
newSettingValue = "JWPlayer";
newSettingNameArray = newSettingName.split("_");
a = Settings;
for (var i = 0 in newSettingNameArray) {
var x = newSettingNameArray[i];
a[x] = i == newSettingNameArray.length-1 ? newSettingValue : {};
a = a[x];
}
I found #jlgrall's answer was great but after simplifying it, it didn't work in Chrome. Here's my fixed should anyone want a lite version:
var callback = 'fn.item1.item2.callbackfunction',
cb = callback.split('.'),
baseObj = window;
function createNestedObject(base, items){
$.each(items, function(i, v){
base = base[v] = (base[v] || {});
});
}
callbackFunction = createNestedObject(baseObj, cb);
console.log(callbackFunction);
I hope this is useful and relevant. Sorry, I've just smashed this example out...
You can define your own Object methods; also I'm using underscore for brevity:
var _ = require('underscore');
// a fast get method for object, by specifying an address with depth
Object.prototype.pick = function(addr) {
if (!_.isArray(addr)) return this[addr]; // if isn't array, just get normally
var tmpo = this;
while (i = addr.shift())
tmpo = tmpo[i];
return tmpo;
};
// a fast set method for object, put value at obj[addr]
Object.prototype.put = function(addr, val) {
if (!_.isArray(addr)) this[addr] = val; // if isn't array, just set normally
this.pick(_.initial(addr))[_.last(addr)] = val;
};
Sample usage:
var obj = {
'foo': {
'bar': 0 }}
obj.pick('foo'); // returns { bar: 0 }
obj.pick(['foo','bar']); // returns 0
obj.put(['foo', 'bar'], -1) // obj becomes {'foo': {'bar': -1}}
A snippet for those who need to create a nested objects with support of array keys to set a value to the end of path. Path is the string like: modal.product.action.review.2.write.survey.data. Based on jlgrall version.
var updateStateQuery = function(state, path, value) {
var names = path.split('.');
for (var i = 0, len = names.length; i < len; i++) {
if (i == (len - 1)) {
state = state[names[i]] = state[names[i]] || value;
}
else if (parseInt(names[i+1]) >= 0) {
state = state[names[i]] = state[names[i]] || [];
}
else {
state = state[names[i]] = state[names[i]] || {};
}
}
};
Set Nested Data:
function setNestedData(root, path, value) {
var paths = path.split('.');
var last_index = paths.length - 1;
paths.forEach(function(key, index) {
if (!(key in root)) root[key] = {};
if (index==last_index) root[key] = value;
root = root[key];
});
return root;
}
var obj = {'existing': 'value'};
setNestedData(obj, 'animal.fish.pet', 'derp');
setNestedData(obj, 'animal.cat.pet', 'musubi');
console.log(JSON.stringify(obj));
// {"existing":"value","animal":{"fish":{"pet":"derp"},"cat":{"pet":"musubi"}}}
Get Nested Data:
function getNestedData(obj, path) {
var index = function(obj, i) { return obj && obj[i]; };
return path.split('.').reduce(index, obj);
}
getNestedData(obj, 'animal.cat.pet')
// "musubi"
getNestedData(obj, 'animal.dog.pet')
// undefined
Try this: https://github.com/silkyland/object-to-formdata
var obj2fd = require('obj2fd/es5').default
var fd = obj2fd({
a:1,
b:[
{c: 3},
{d: 4}
]
})
Result :
fd = [
a => 1,
b => [
c => 3,
d => 4
]
]
Here is a decomposition to several useful functions, that each preserve existing data. Does not handle arrays.
setDeep: Answers question. Non-destructive to other data in the object.
setDefaultDeep: Same, but only sets if not already set.
setDefault: Sets a key if not already set. Same as Python's setdefault.
setStructure: Helper function that builds the path.
// Create a nested structure of objects along path within obj. Only overwrites the final value.
let setDeep = (obj, path, value) =>
setStructure(obj, path.slice(0, -1))[path[path.length - 1]] = value
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setDefaultDeep = (obj, path, value) =>
setDefault(setStructure(obj, path.slice(0, -1)), path[path.length - 1], value)
// Set obj[key] to value if key is not in object, and return obj[key]
let setDefault = (obj, key, value) =>
obj[key] = key in obj ? obj[key] : value;
// Create a nested structure of objects along path within obj. Does not overwrite any value.
let setStructure = (obj, path) =>
path.reduce((obj, segment) => setDefault(obj, segment, {}), obj);
// EXAMPLES
let temp = {};
// returns the set value, similar to assignment
console.log('temp.a.b.c.d:',
setDeep(temp, ['a', 'b', 'c', 'd'], 'one'))
// not destructive to 'one'
setDeep(temp, ['a', 'b', 'z'], 'two')
// does not overwrite, returns previously set value
console.log('temp.a.b.z: ',
setDefaultDeep(temp, ['a', 'b', 'z'], 'unused'))
// creates new, returns current value
console.log('temp["a.1"]: ',
setDefault(temp, 'a.1', 'three'))
// can also be used as a getter
console.log("temp.x.y.z: ",
setStructure(temp, ['x', 'y', 'z']))
console.log("final object:", temp)
I'm not sure why anyone would want string paths:
They are ambiguous for keys with periods
You have to build the strings in the first place
Since I started with something from this page, I wanted to contribute back
Other examples overwrote the final node even if it was set, and that wasn't what I wanted.
Also, if returnObj is set to true, it returns the base object. By default, falsy, it returns the deepest node.
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
if (!(key in child)) {
child[key] = (i < path.length-1) ? {} : value || {};
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
console.log(xOut);
You can also do something where numeric keys are placed in arrays (if they don't already exist). Note that numeric keys won't convert to arrays for the first element of the path, since that's set by the type of your base-object.
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
}
function param(obj, path, value, returnObj) {
if (typeof path == 'string') path = path.split(".");
var child = obj;
path.forEach((key, i) => {
var nextKey = path[i+1];
if (!(key in child)) {
child[key] = (nextKey == undefined && value != undefined
? value
: isNumber(nextKey)
? []
: {});
}
child = child[key];
});
return returnObj ? obj : child;
}
var x = {};
var xOut = param(x, "y.z", "setting")
console.log(xOut);
xOut = param(x, "y.z", "overwrite") // won't set
console.log(xOut);
xOut = param(x, "y.a", "setting2")
console.log(xOut);
xOut = param(x, "y.a", "setting2", true) // get object rather than deepest node.
xOut = param(x, "1.0.2.a", "setting")
xOut = param(x, "1.0.1.a", "try to override") // won't set
xOut = param(x, "1.0.5.a", "new-setting", true) // get object rather than deepest node.
console.log(xOut);
Naturally, when the numeric keys are greater than 0, you might see some undefined gaps.
Practical uses of this might be
function AddNote(book, page, line) {
// assume a global global notes collection
var myNotes = param(allNotes, [book, page, line], []);
myNotes.push('This was a great twist!')
return myNotes;
}
var allNotes = {}
var youthfulHopes = AddNote('A Game of Thrones', 4, 2, "I'm already hooked, at least I won't have to wait long for the books to come out!");
console.log(allNotes)
// {"A Game of Thrones": [undefined, undefined, undefined, undefined, [undefined, undefined, ["I'm already hooked, at least I won't have to wait long for the books to come out!"]]]}
console.log(youthfulHopes)
// ["I'm already hooked, at least I won't have to wait long for the books to come out!"]
function initPath(obj, path) {
path.split('.').reduce((o, key) => (
Object.assign(o, {[key]: Object(o[key])}),
o[key]
), obj);
return obj;
}
Usage
const obj = { a: { b: 'value1' } };
initPath(obj, 'a.c.d').a.c.d='value2';
/*
{
"a": {
"b": "value1",
"c": {
"d": "value2"
}
}
}
*/
simple answer. on es6, im using this
const assign = (obj, path, value) => {
let keyPath = path.split('.')
let lastKeyIndex = keyPath.length - 1
for (let i = 0; i < lastKeyIndex; ++i) {
let key = keyPath[i]
if (!(key in obj)) {
obj[key] = {}
}
obj = obj[key]
}
obj[keyPath[lastKeyIndex]] = value
}
example json
const obj = {
b: 'hello'
}
you can add new key
assign(obj, 'c.d.e', 'this value')
and you get like bellow
console.log(obj)
//response example
obj = {
b: 'hello',
c: {
d: {
e: 'this value'
}
}
}
function createObj(keys, value) {
let obj = {}
let schema = obj
keys = keys.split('.')
for (let i = 0; i < keys.length - 1; i++) {
schema[keys[i]] = {}
schema = schema[keys[i]]
}
schema[keys.pop()] = value
return obj
}
let keys = 'value1.value2.value3'
let value = 'Hello'
let obj = createObj(keys, value)
Eval is probably overkill but the result is simple to visualize, with no nested loops or recursion.
function buildDir(obj, path){
var paths = path.split('_');
var final = paths.pop();
for (let i = 1; i <= paths.length; i++) {
var key = "obj['" + paths.slice(0, i).join("']['") + "']"
console.log(key)
eval(`${key} = {}`)
}
eval(`${key} = '${final}'`)
return obj
}
var newSettingName = "Modules_Video_Plugin_JWPlayer";
var Settings = buildDir( {}, newSettingName );
Basically you are progressively writing a string "obj['one']= {}", "obj['one']['two']"= {} and evaling it;

More efficient way to find max of multiple like-objects in array JS?

Is there an array method (or any other way really) to group objects within an array by a property and sum by another. Then return the max?
let array = [
{ticker:'AAPL',val:400},
{ticker:'IBM',val:200},
{ticker:'AAPL',val:500},
{ticker:'SBUX',val:800},
]
let obj = {}
array.forEach( h => {
obj[h.ticker] = obj[h.ticker] || 0;
obj[h.ticker] += h.val
})
let temp = Object.keys( obj ).map(function ( key ) { return obj[key] });
console.log(Math.max(...temp)) // returns 900
If you just want to modify it to use reduce instead of forEach, you can do:
let obj = array.reduce((a,b) => ({...a, [b.ticker]: (a[b.ticker] || 0) + b.val}), {});

Need to iterate through an array to get specific key: value pairs to add to empty object

For this problem, the function accepts an array of strings and returns an object. Keys are supposed to be the number of characters in a string and the value is supposed to be how many time a string with that amount of characters occurred.
I thought I was going somewhere and then I got stuck. I'd appreciate some help on this, I've tried googling it a million different ways but no luck. Thank you!
The result is supposed to look like : characterCount(['apple', 'berry', 'cherry']) // {5:2, 6:1}
function characterCount(arr){
var newObj = {};
var valueMax = 0;
var currentValue = 0;
for(var i=0; i < arr.length; i++){
var key = arr[i].length;
for(var z=0; z < arr.length; z++){
if (arr[z].length === arr[i].length){
currentValue ++;
if (currentValue > valueMax){
valueMax = currentValue;
}
}
}
newObj.key = "valueMax";
}
return newObj;
}
Look at the Array.prototype.reduce function. This allows you to take an array, iterate over each value, and return a new, reduced value.
function characterCount(arr) {
return arr.reduce((counts, str) => ({
...counts,
[str.length]: (counts[str.length] || 0) + 1
}), {});
}
const counts = characterCount(['apple', 'berry', 'cheery']);
console.log(counts);
Alternatively, you could use Object.assign instead of spreading the accumulator object.
function characterCount(arr) {
return arr.reduce((counts, str) => Object.assign(counts, {
[str.length]: (counts[str.length] || 0) + 1
}), {});
}
const counts = characterCount(['apple', 'berry', 'cheery']);
console.log(counts);
You could just reduce the array to accomplish the output
function characterCount( array ) {
return array.reduce( (agg, cur) => {
// get the length of the current item
const len = cur.length;
// increase the value of the key index with one (if none exist, start with 0)
agg[len] = (agg[len] || 0) + 1;
// return the next value for the iteration
return agg;
}, {});
}
console.log( characterCount(['apple', 'berry', 'cherry']) );
Using reduce is arguably better but here is a more straightforward approach.
function characterCount(arr) {
const countByLength = {};
for (let item of arr) {
countByLength[item.length] = (countByLength[item.length] || 0) + 1;
}
return countByLength;
}
console.log(characterCount(['apple', 'berry', 'cherry']));

Convert String Into Delimited Object Key

I am trying to convert a string into a delimited object key but I need some assistance on how to iterate over the length of the array and join accordingly.
SET('my.delimited.string.of.unknown.length')
const SET = key => (state, val) => {
if(key.indexOf('.') !== -1) {
let array = key.split(".")
for (var i = 0; i < array.length; i++) {
// what should I do here?
}
// desired output based on array length
// state[ array[0] ][ array[1] ] = val
// state.my.delimited.string.of.unknown.length = val
}
}
One of those very rare usecases for reduce:
const keys = key.split(".");
const prop = keys.pop();
keys.reduce((acc, key) => acc[key], state)[prop] = val;
For sure that could also be done with a for loop:
let array = key.split("."), acc = state;
for (var i = 0; i < array.length - 1; i++) {
acc = acc[ array[i] ];
}
acc[ array.pop() ] = val;
For setting a value, you could split the path and reduce the path by walking the given object. If no object exist, create a new property with the name. Later assign the value.
function setValue(object, path, value) {
var keys = path.split('.'),
last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, object)[last] = value;
}
var test = {};
setValue(test, "first.deep.property", 1);
setValue(test, "and.another.deep.property", 20);
console.log(test);
You could also do this with a single Array.reduce:
const makeObject = (arr, val, obj={}) => {
arr.split('.').reduce((r,c,i,a) => r[c] = i == a.length-1 ? val : {}, obj)
return obj
}
console.log(makeObject("first.deep.property", 1))
console.log(makeObject("and.another.deep.property", 20))

Javascript sorting Object dates

After looping I got following array. Is it possible to sort this from current date to latest.
//Sortering
var arr = {};
var key = "";
var teller = 0;
for (var i = 0; i < schedule_id.length; i++) {
//Ajax call maken
$.ajax({
url: "http://api.viewer.zmags.com/schedules/" + schedule_id[i] + "?key=" + api_key
})
//WdInit after 10 calls
.done(function(data){
//Check publicatieID is not null
if (undefined === data.scheduleEntries[default_pub]|| null === data.scheduleEntries[default_pub]) {
}
else
{
var key = schedule_id[teller];
//loopen doorheen resultaat call
$.each(data.scheduleEntries, function(index, entry){
arr[key] = entry.startDate;
})
}
teller++;
})
}
arr: Object
7aaabbec: "2015-02-09T23:00:00.000Z"
31ba19e7: "2015-02-24T23:01:00.000Z"
31ff78e7: "2015-02-24T23:01:00.000Z"
159a11a7: "2015-02-10T23:01:00.000Z"
1339d0e9: "2015-02-17T23:01:00.000Z"
Code that I already got but error: Undefined is not a function
arr.sort(function(a, b) {
return a - b;
});
Objects have no order. You cannot order an object. You'd have to turn this into an array first, which in turn cannot have arbitrary keys, but is orderable.
In Javascript:
objects: key-value collections with undefined order
arrays: lists with order, but without (meaningful) keys
First get keys from object, you use something like underscore.js for this:
var keyList = _.keys( arr );
// sort keyList
keyList = keyList.sort();
// Now, do anything by getting values for sorted keys
for ( key in keyList ) {
console.log( arr[ key ] );
}
Just read your latest comment... for your case you can do it by first converting into a list of lists or list of key-val pairs
// convert into List of [ key, value ]
val pairList = _.pairs( arr );
// sort using values
pairList = pairList.sort( function( a, b ) {
// use moment.js to get date functions
var aMoment = moment( a[ 1 ] )
var bMoment = moment( b[ 1 ] )
// change based on reverse-sorted or sorted.
return bMement.diff( aMement );
} );
// Now sorted use as you want
for ( keyVal in pairList ) {
console.log( keyVal[ 0 ] + " -> " + keyVal[ 0 ] );
}
Use normal array to store the values so you can order them:
arr = [];
loopen doorheen resultaat call
$.each(data.scheduleEntries, function(index, entry){
arr.push( {'key':key, 'date':entry.startDate} );
})
arr.sort(function(a, b){
return a['date'] > b['date']; //this may need a better comparision...
});
UPDATE:
to extract the hash keys after the sorting just loop the array:
var sorted_keys = [];
for(var i = 0; i < arr.length; i++){
sorted_keys.push( arr[i] );
}
By looping object, get all dates into array and sort them and make the object again
var myObj = {
'7aaabbec': "2015-02-09T23:00:00.000Z",
'31ba19e7': "2015-02-24T23:01:00.000Z",
'31ff78e7': "2015-02-24T23:01:00.000Z",
'159a11a7': "2015-02-10T23:01:00.000Z",
'1339d0e9': "2015-02-17T23:01:00.000Z"
};
var timeArray = [], newObj = {};
for(var key in myObj){
timeArray.push([key,myObj[key]]);
}
timeArray.sort(function(a, b) {return new Date(a[1]) - new Date(b[1])});
//console.log(timeArray);
var j=0,k=1;
for(var i=0;i<timeArray.length;i++){
newObj[timeArray[i][j]] = new Date(timeArray[i][k]);
}
$("#result").html(JSON.stringify(newObj));
Working example is here

Categories