Methods to insert or replace values in to object - javascript

I've got an object like this
var obj2= {
'home' : {
'1' : {
'year' : '1999'
},
'2' : {
'year' : '2000'
'month' : '11'
},
},
'company' : {
'1' : {
'year' : '2010'
'month' : '2'
},
}
};
And I would like to add some more keys inside the object or replace value if this key chain already exists, for ex:
buss_type = "home",
type_id = "1",
date_type = "month",
value = "9"
var tmp_obj1 = {[buss_type]: {[type_id]: {[date_type]: value}}};
and
buss_type = "company",
type_id = "2",
date_type = "month",
value = "12"
var tmp_obj2 = {[buss_type]: {[type_id]: {[date_type]: value}}};
to make object:
obj = {
'home' : {
'1' : {
'year' : '1999',
'month' : '9'
},
'2' : {
'year' : '2000',
'month' : '11'
},
},
'company' : {
'1' : {
'year' : '2020',
'month' : '10'
},
'2' : {
'month' : '12'
}
},
};
Arrays methods like .push() or .concat() adding new keys on the end only instead of replacing values of existing ones.
Are there any object methods that can make it easy to manage or should I just use loops and check all keys before adding new or changing value of existing one?
Thanks.

You could take an array for the keys and save the last key for direct assignment.
function setValue(object, path, value) {
var last = path.pop();
path.reduce(function (o, k) {
return o[k] = o[k] || {};
}, object)[last] = value;
}
var object = { home: { 1: { year: '1999' }, 2: { year: '2000', month: '11' } }, company: { 1: { year: '2010', month: '2' } } };
setValue(object, ["home", "1", "month"], "9");
setValue(object, ["company", "2", "month"], "12");
console.log(object);
.as-console-wrapper { max-height: 100% !important; top: 0; }

You can use reduce() method to add nested properties to object.
var obj2 = {"home":{"1":{"year":"1999"},"2":{"year":"2000","month":"11"}},"company":{"1":{"year":"2010","month":"2"}}}
function add(obj, value, ...keys) {
keys.reduce(function(r, e, i, arr) {
return r[e] || (r[e] = arr[i + 1] ? {} : value)
}, obj)
}
let buss_type = "home", type_id = "1", date_type = "month", value = "9";
add(obj2, value, buss_type, type_id, date_type)
buss_type = "company", type_id = "2", date_type = "month", value = "12"
add(obj2, value, buss_type, type_id, date_type)
console.log(obj2)

If you will only have objects without any methods inside, just simple JSON style objects. Then you can use something like
JSON.stringify(obj1) === JSON.stringify(obj2)
Just be careful that the order of the object data is important. For example, this will return false
x = {a: 1, b: 2};
y = {b: 2, a: 1};

You don't need to create a loop instead you can programmatically access the key you're about to add using this syntax:
buss_type = "home",
type_id = "1",
date_type = "month",
value = "9"
if(obj[buss_type]){
if(obj[buss_type][type_id]){
obj[buss_type][type_id][date_type] = value
}else{
obj[buss_type][type_id] = { date_type: value }
}
}else{
obj[buss_type] = { type_id: { date_type: value } }
}

Related

Tree json "menu" with nested items : check the "branch"

I have this JSON tree view that represents a menu :
var menus = [
{
label : "1",
items: [
{
label : "1.1"
},
{
label : "1.2",
items : [
{
label : "1.2.1"
},
{
label : "1.2.2"
}
]
},
{
label : "1.3"
},
]
},
{
label : "2"
}
]
I want to mutate this JSON by adding for each item a selected property. This property, a boolean, will be set to true if the label is the right one or this is the tricky part if the descendant is the right one.
For instance, if I'm looking for label 1.2, all labels 1 and 1.2 will be selected. So I will get this JSON :
var menus = [
{
label : "1",
selected : true,
items: [
{
label : "1.1"
selected : false
},
{
label : "1.2",
selected : true,
items : [
{
label : "1.2.1"
selected : false
},
{
label : "1.2.2",
selected : false
}
]
},
{
label : "1.3",
selected : false
},
]
},
{
label : "2",
selected : false
}
]
the selected : false is not necessary.
Lodash is OK for me;)!
Any suggestions?
edit : where I am ! --> https://codepen.io/anon/pen/XGoXjM?editors=0010
edit 2 : finding elements must not be based on the way I wrote the labels. The labels can be any string... Sorry...
Thanks
This solution uses a for loop to iterate recursively the menu items and their children. If an item is selected, it adds selected: true to the item and it's parents:
const selectMenuItems = menus => selectedLabel => {
const internal = arr => {
let selected = false
for(let i = 0; i < arr.length; i++) {
const item = arr[i]
const childrenSelected = !!item.items && internal(item.items)
item.selected = childrenSelected || item.label === selectedLabel
selected = selected || item.selected
}
return selected
}
internal(menus)
return menus
}
const menus = [{"label":"1","items":[{"label":"1.1"},{"label":"1.2","items":[{"label":"1.2.1"},{"label":"1.2.2"}]},{"label":"1.3"}]},{"label":"2"}]
const menuSelector = selectMenuItems(menus)
const result = menuSelector('1.2')
console.log(result)
.as-console-wrapper { top: 0; max-height: 100% !important; }
I would simply check labels this way:
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
var checkSelected = function(items, search) {
for (var key in items) {
items[key].selected = search.startsWith(items[key].label) && items[key].label.length<=search.length;
if (items[key].items) {
checkSelected(items[key].items, search);
};
};
};
var test = "1.2";
checkSelected(menus, test);
console.log(menus);
Also on JSFiddle.
The startsWith() method determines whether a string begins with the
characters of a specified string, returning true or false as
appropriate.
quoted from here
You can use some recursive approach to implement this.
let str = '1.2.1';
function checkItem(arr, strArr) {
// iterate over the array
arr.forEach((obj) => {
// set selected property based on matching every digit in label in same order
// if digits would be single then you can use startsWith and no need to split string
obj.selected = obj.label.split('.').every((it, i) => it === strArr[i]);
// if nested item is there then call recursively
obj.items && checkItem(obj.items, strArr);
});
return arr;
}
checkItem(menus, str.split('.'));
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
let str = '1.2.1';
function checkItem(arr, strArr) {
arr.forEach((obj) => {
obj.selected = obj.label.split('.').every((it, i) => it === strArr[i]);
obj.items && checkItem(obj.items, strArr);
});
return arr;
}
checkItem(menus, str.split('.'));
console.log(menus);
UPADATE : Since you want to update selected property completely independent of label you can do something like follows. I assume you want to
update based on position in the array.
let str = '1.2.1';
function checkItem(arr, prefixArray, strArr) {
// iterate over the array
arr.forEach((obj, i) => {
// generate new prefix array for checking
let pa = [...prefixArray, i + 1];
// compare prefix array with the string array to check matches
obj.selected = pa.every((it, i) => it == strArr[i]);
// if items defined do it recursively
obj.items && checkItem(obj.items, pa, strArr);
});
return arr;
}
checkItem(menus,[], str.split('.'));
var menus = [{
label: "1",
items: [{
label: "1.1"
},
{
label: "1.2",
items: [{
label: "1.2.1"
},
{
label: "1.2.2"
}
]
},
{
label: "1.3"
},
]
},
{
label: "2"
}
];
let str = '1.2.1';
function checkItem(arr, prefixArray, strArr) {
arr.forEach((obj, i) => {
let pa = [...prefixArray, i + 1];
obj.selected = pa.every((it, i) => it == strArr[i]);
obj.items && checkItem(obj.items, pa, strArr);
});
return arr;
}
checkItem(menus,[], str.split('.'));
console.log(menus);

Best way to map this object into a new object

Using lodash or vanilla javascript, I need to convert this json:
{
"results" : [
{
"values" : [
{"label" : "FOO", "value" : "foovalue" },
{"label" : "BAR", "value" : "barvalue1" },
{"label" : "BAR", "value" : "barvalue2" }
]
},
{
"values" : [
{"label" : "FOO", "value" : "foovalue"},
{"label" : "BAR", "value" : "barvalue1"},
{"label" : "BAR", "value" : "barvalue2"}
]
}
]
}
into a new object where the label values become the key and duplicates are made into an array of new objects like this:
[
[{"FOO" : "foovalue", "BAR" : ["barvalue1", "barvalue2"]}],
[{"FOO" : "foovalue", "BAR" : ["barvalue1", "barvalue2"]}]
]
I've tried using _.map but it is overwriting the duplicate values and I need them all in an array.
Here is the closest I've come:
var arr = _.map(results, function(o) {
return _.map(o.values, function(v) {
var obj = {};
obj[t.label] = t.value;
return obj;
});
});
where arr returns an array of the objects like this (with the objects being overwritten and not combined into a single object) [{"FOO" : "foovalue"},{"BAR" : "barvalue2"}] and I'm stuck trying to make them into the above array.
You could map the outer array and reduce the inner array by collecting the values of labels. If more than one collected element use an array.
var results = [{ values: [{ label: "FOO", value: "foovalue" }, { label: "BAR", value: "barvalue1" }, { label: "BAR", value: "barvalue2" }] }, { values: [{ label: "FOO", value: "foovalue" }, { label: "BAR", value: "barvalue1" }, { label: "BAR", value: "barvalue2" }] }],
grouped = results.map(({ values }) => [
values.reduce((o, { label, value }) => {
if (!o[label]) {
o[label] = value;
return o;
}
if (!Array.isArray(o[label])) {
o[label] = [o[label]];
}
o[label].push(value);
return o;
}, {})
]);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

getting a field from a json object using a "address string" in JavaScript

I am new to java script , so my apologies if this is trivial.
My question is syntactical, if I have an object:
this.state = {
A : {
B: {
C : [
{value : 'bob'},
{value : 'Jim'},
{value : 'luke'},
]
}
}
}
and I have a string location = 'A.B.C[1]' which describes the location of the data I want.
Why can I not just do data = this.state[location].value ?
and is there a simple "JavaScript" way of getting the data using the location string?
any help would be amazing :)
You could split the path and reduce the object.
function getValue(o, path) {
return path.replace(/\[/g, '.').replace(/\]/g, '').split('.').reduce(function (o, k) {
return (o || {})[k];
}, o);
}
var o = { A : { B: { C: [{ value: 'Brenda' }, { value: 'Jim' }, { value: 'Lucy' }] } }};
console.log(getValue(o, 'A.B.C[1]').value); // Jim
console.log(getValue(o, 'A.B.C[0].value')); // Brenda
console.log(getValue(o, 'Z[0].Y.X[42]')); // undefined
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, or an array. Later assign the value.
function setValue(object, path, value) {
var way = path.replace(/\[/g, '.').replace(/\]/g, '').split('.'),
last = way.pop();
way.reduce(function (o, k, i, kk) {
return o[k] = o[k] || (isFinite(i + 1 in kk ? kk[i + 1] : last) ? [] : {});
}, object)[last] = value;
}
var test = {};
setValue(test, "foo.name", "Mr. Foo");
setValue(test, "foo.data[0].bar", 100);
setValue(test, "and.another[2].deep", 20);
console.log(test);

Count elements that have the same value for a specific property and put the result in an array of objects

Using Array.reduce, I am trying to count the elements that have the same value for a specific property. I want to put the result in an array of objects containing a property for the value of the grouped by property and another one for the count. How can I do this easily in javascript ?
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [
{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}
]
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
console.log(categoryLessonCount[CATEGORY.STRATEGY])
console.log(categoryLessonCount[CATEGORY.CONTENT])
Actual categoryLessonCount value :
Object
{
STRATEGY: 1,
CONTENT: 2
}
Wanted categoryLessonCount value :
Array
[
{
title: 'STRATEGY',
count: 1
}, {
title: 'CONTENT',
count: 2
}
]
You already got the what you want just transform it into an array
const CATEGORY = {
STRATEGY: 'STRATEGY',
CONTENT: 'CONTENT',
ADVERTISING: 'ADVERTISING',
MEASURMENT: 'MEASURMENT'
}
const lessons = [{
title: 'ohoho',
category: CATEGORY.STRATEGY
}, {
title: 'hihihi',
category: CATEGORY.CONTENT
}, {
title: 'hello',
category: CATEGORY.CONTENT
}]
let count = lessons.reduce(function(acc, lesson) {
acc[lesson.category] ? acc[lesson.category] ++ : acc[lesson.category] = 1
return acc
}, {})
// transform count into what you want
let categoryLessonCount = [];
for (let cat in count) {
categoryLessonCount.push({
'title': cat,
'count': count[cat]
});
}
console.log(categoryLessonCount)
Something like this should work:
let categoryLessonCount = lessons.reduce(function(acc, lesson) {
let found = false
for (const item of acc) {
if (item.title === lesson.category) {
item.count++
found = true
}
}
if (!found) {
acc.push({
title: lesson.category,
count: 1
})
}
return acc
}, [])
Your main issue is that your accumulating an object but expecting an array (note the final argument to reduce).
Short solution using Object.keys and Array.prototype.map functions:
...
let categoryLessonCount = lessons.reduce(function (acc, lesson) {
acc[lesson.category] ? acc[lesson.category]++ : acc[lesson.category] = 1
return acc
}, {})
let counts = Object.keys(categoryLessonCount).map(
(k) => ({title: k, count: categoryLessonCount[k]})
)
console.log(counts);

Check if there are null values in an array of objects

I have this array of objects.
[Object, Object, Object]
0:Object
name: "Rick"
Contact: "Yes"
Date:'null'
Location:'null'
1:Object
name:"Anjie"
Contact:"No"
Date:'13/6/2016'
Location:'LA'
2:Object
name:"dillan"
Contact:"Maybe"
Date:'17/6/2016'
Location:'NY'
As you can see, there are null values for Object[0] for Date and Location. I want to check if there is a null value present in the entire array of Objects.
If there is a null value present for 'Date' and 'Location', i should be able to display 'Null present' at the console. If no null values are present, it should display 'Data right' at the console.
can someone please let me know how to achieve this.
var wasNull = false;
for(var i in objectsArray) {
if(objectsArray[i].Date == null || objectsArray[i].Location == null) wasNull = true;
}
if(wasNull) console.log('Was null');
else console.log('Data right');
Do it using Object.keys() and Array#some methods
var data = [{
name: "Rick",
Contact: "Yes",
Date: null,
Location: null
}, {
name: "Anjie",
Contact: "No",
Date: '13/6/2016',
Location: 'LA'
}, {
name: "dillan",
Contact: "Maybe",
Date: '17/6/2016',
Location: 'NY'
}];
// iterate over array elements
data.forEach(function(v, i) {
if (
// get all properties and check any of it's value is null
Object.keys(v).some(function(k) {
return v[k] == null;
})
)
console.log('null value present', i);
else
console.log('data right', i);
});
Use some() and check if there is a null.
var arr = [
{ a : "a", b : "b", c : "c" },
{ a : "a", b : "b", c : "c" },
{ a : "a", b : "b", c : null }
];
function hasNull(element, index, array) {
return element.a===null || element.b===null || element.c===null;
}
console.log( arr.some(hasNull) );
If you do not want to hardCode the if, than you need to add another loop and loop over the keys.
var arr = [
{ a : "a1", b : "b1", c : "c1" },
{ a : "a2", b : "b2", c : "c2" },
{ a : "a3", b : "b3", c : null }
];
function hasNull(element, index, array) {
return Object.keys(element).some(
function (key) {
return element[key]===null;
}
);
}
console.log( arr.some(hasNull) );
or JSON with a reg exp (
var arr = [
{ a : "a1", b : "b1", c : "c1" },
{ a : "a2", b : "b2", c : "c2" },
{ a : "a3", b : "b3", c : null }
];
var hasMatch = JSON.stringify(arr).match(/:null[\},]/)!==null;
console.log(hasMatch);
A solution using underscore:
var nullPresent = _.some(data, item => _.some(_.pick(item, 'Date', 'Location'), _.isNull));

Categories

Resources