I have an object as follows:
let object = {
1: {id: 1, name: "One"},
2: {id: 2, name: "Two"},
3: {id: 3, name: "Three"}
}
And I want to remove one of them, for example the object with id 2
Now I do it using lodash as follows:
forOwn(object, function(value, key) {
value.id === 2 && delete object[key]
});
That does the trick but is it the right way?
You can use UnderscoreJs Library
let object = {
1: {id: 1, name: "One"},
2: {id: 2, name: "Two"},
3: {id: 3, name: "Three"}
}
let newobject=_.remove(object,function(nv){
return nv.id===3;
});
the above code will delete the object having id=3 and return
{
1: {id: 1, name: "One"},
2: {id: 2, name: "Two"},
}
I think you're not getting the answers you're looking for, because maybe you've over simplified the use-case, so it's tempting to just say:
delete object[2]; // which modifies the original object
If your data is not that static, or if you want to do something more complex to remove certain elements, you could do something similar to this:
const relevantObjects = Object.entries(object) // converts each entry to [key, value]
.filter(([k, v]) => v.id !== 2) // define the criteria to include/exclude items
.reduce((acc, [k, v]) => {
acc[k] = v;
return acc; // this function can be improved, it converts the [[k, v]] back to {k: v, k: v, ...}
}, {});
Edit:
It's perfectly fine to use a lib or whatever. Everything has its pros & cons, just do whatever works for you <3
I would use a simple for ... in instead of loadash
let object = {
1: {id: 1, name: "One"},
2: {id: 2, name: "Two"},
3: {id: 3, name: "Three"}
}
let deleteById = (obj,idToRemove) => {
for(let key in obj){
let {id} = obj[key] | {}
if(id === idToRemove){
delete obj[key]
}
}
}
deleteById(object,2)
console.log(object)
This is the right way. To do it in JSON you do the following:
var json = { ... };
var key = "foo";
delete json[key];
So if you swap out json to object you are all set.
Related
I'm having some trouble formatting/transforming some simple data into a format that I can use to graph, and I'm hoping someone might help me solve. Currently, I have something like this
somedata=
{test1: {good: 3, bad: 2, redo: 2}}
{test2: {good: 4, bad: 3}}
{test3: {good: 3, redo: 4}}
into something like
series:
[{name: "good", data: [3,4,3]},
{name: "bad", data: [2,3,0]},
{name: "redo", data: [2,0,4]}]
I can grab the categories by using Object.keys(somedata) easy enough i.e. ['test1', 'test2', 'test3'] but having problem formatting the rest of the data. I tried something like
let combine = {};
Object.values(somedata).map((row) => {
for (const [key, value] of Object.entries(row)) {
combine.hasOwnProperty(key)
? combine[key].push(value)
: (combine[key] = [value]);
}
console.log("combined", combine);
});
but quickly realized that it won't add 0 when key doesn't exist, which is required for the chart to compare between the different series, such as bar charts. So, any help is appreciated.
You can first collect all unique values and then using array#reduce and other array methods generate all the values corresponding to each key in an object accumaltor.
const somedata = [{test1: {good: 3, bad: 2, redo: 2}}, {test2: {good: 4, bad: 3}}, {test3: {good: 3, redo: 4}}],
uniqueValues = [...new Set(
somedata.reduce((r,o) => {
Object.values(o).forEach(ob => {
r.push(...Object.keys(ob));
});
return r;
}, [])
)];
result = Object.values(somedata.reduce((r, o) => {
Object.values(o).forEach(ob => {
uniqueValues.forEach(k => {
r[k] = r[k] || { name: k, data: []};
ob[k] ? r[k].data.push(ob[k]): r[k].data.push(0);
});
});
return r;
},{}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
something like this: for each test, group categories (can optionally restrict to a subset) - assume Zero for missing category
const someData = {test1: {good: 3, bad: 2, redo: 2}, test2: {good: 4, bad: 3}, test3: {good: 3, redo: 4}};
function prepMyGraphData(data, fields) {
let out = {
}
for (const [k, el] of Object.entries(data)) {
const _fields = new Set((fields || Object.keys(el)).concat(Object.keys(out)));
for (const f of _fields) {
const v = el.hasOwnProperty(f) ? el[f] || 0 : 0 ; // own field or 0
if (out.hasOwnProperty(f)) {
out[f].data.push(v) // existing category
}else{
out[f] = {name: f, data: [v]} // new category entry
}
}
}
return Object.values(out)
}
let fields = ['good', 'bad', 'redo']; // OR, undefined, for ALL own properties
const data = prepMyGraphData(someData, fields);
I am trying to use array.filter() to compare two arrays and separate out values that the two arrays have in common, based on a certain property (id), vs. values they don't have in common. The common ids I want to push to a new array (recordsToUpdate). And I want to push the remaining elements from arr2 to a new array (recordsToInsert).
What I've tried is not working. How can I rework this to get the results I wanted? - (which in the example here should be one array of 1 common element {id: 3}, and another array of the remaining elements from arr2):
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
let recordsToUpdate = [];
let recordsToInsert = [];
recordsToUpdate = arr1.filter(e => (arr1.id === arr2.id));
recordsToInsert = ?
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
The desired result should be:
recordsToUpdate = [{id: 3}];
recordsToInsert = [{id: 4}, {id: 5}];
Try this, which uses Array.prototype.find to test for whether an object exists in arr2 with a given id:
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
const recordsToUpdate = arr1.filter(e => arr2.find(obj => obj.id === e.id) !== undefined);
const recordsToInsert = arr1.filter(e => arr2.find(obj => obj.id === e.id) === undefined);
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
Update to Robin post using some instead of find. It is just other way around.
const arr1 = [{id: 1}, {id: 2}, {id: 3}];
const arr2 = [{id: 3}, {id: 4}, {id: 5}];
const recordsToUpdate = arr1.filter(e => arr2.some(obj => obj.id === e.id));
const recordsToInsert = arr2.filter(e => !arr1.some(obj => obj.id === e.id));
console.log('recordsToUpdate: ', recordsToUpdate);
console.log('recordsToInsert: ', recordsToInsert);
I think this is what you are after... I added values to show the replacement. If you are doing any kind of state management, be careful as I am directly mutating the current array.
const arr1 = [
{ id: 1, v: "a" },
{ id: 2, v: "b" },
{ id: 3, v: "old" }
];
const arr2 = [
{ id: 3, v: "new" },
{ id: 4, v: "e" },
{ id: 5, v: "f" }
];
function updateRecords(currentArray, updatesArray) {
const currentIds = currentArray.map(item => item.id);
updatesArray.forEach(updateItem =>
currentIds.includes(updateItem.id)
? (currentArray[
currentIds.findIndex(id => id === updateItem.id)
] = updateItem)
: currentArray.push(updateItem)
);
return currentArray;
}
console.log(updateRecords(arr1, arr2))
This now gives the option below:
[
{
"id": 1,
"v": "a"
},
{
"id": 2,
"v": "b"
},
{
"id": 3,
"v": "new"
},
{
"id": 4,
"v": "e"
},
{
"id": 5,
"v": "f"
}
]
Putting it in a function is also something you likely want to do as you will likely use this multiple places in your code.
How can I sort an array by string value?
If I have an array such as ['you', 'I', 'me', 'me', 'will', 'me'], how can I get all the indexes with the word me at the front of the array?
I have tried using array.sort, but it does not seem to be working.
e.target.value is the an value I am getting from a <select element in the dom.
arr.sort((a, b) => {
if (a < e.target.value) {
return -1;
}
if (a > e.target.value) {
return 1;
}
return 0;
});
UPDATE:
Yury's answer works very well, but what if I need to match two values in an array of objects and have those sorted.
Example:
arr = [
{id: 1, name: "cookie"},
{id: 2, name: 'foo'},
{id: 3, name: 'bar'},
{id: 2, name: 'foo'}
];
How can I place all the elements with the id '2' and with the name 'foo' at the front of the array?
You could use sort
let a = ['you', 'I', 'me', 'me', 'will', 'me'];
a.sort((a, b) => a !== b && b === 'me' ? 1 : 0);
console.log(a)
const arr = [
{id: 1, name: "cookie"},
{id: 2, name: 'foo'},
{id: 3, name: 'bar'},
{id: 2, name: 'foo'}
];
Use the Array.prototype.sort() method on arr using a callback function that switches the order of items only if the first one does not match the given criteria and the second one does.
arr.sort((item1, item2) => {
if((item1.id !== 2 || item1.name !== 'foo') && (item2.id === 2 || item2.name === 'foo')) {
return 1;
}
return 0;
});
console.log(arr);
Hey I have the following array :
var ar = [{id: "F"}, {id: "G"}, {id: "Z"}, {id: "ZZ"}]
I would like to move the one with the id equals to ZZ to the first position in the array.
I know how to do it using several different functions, but I was wondering if there was an elegant solution to do it (lodash, ...)
You could use unshift() to add to start of array and splice() and findIndex() to get object by id.
var arr = [{id: "F"}, {id: "G"}, {id: "Z"}, {id: "ZZ"}]
arr.unshift(arr.splice(arr.findIndex(e => e.id == "ZZ"), 1)[0])
console.log(arr)
var ar = [{
id: "F"
}, {
id: "G"
}, {
id: "Z"
}, {
id: "ZZ"
}];
ar.sort(function(a, b) {
if (a.id === "ZZ") {
return -1;
}
if (b.id === "ZZ") {
return 1;
}
return 0;
});
console.log(ar);
How about sorting it?
I have a stupid problem that at first seems to be simple to solve, but turns out to be tricky.
I have an array of objects, each with two properties: id and value:
[
{id: 2, value: 10},
{id: 4, value: 3},
{id: 2, value: 2},
{id: 1, value: 15}
]
I want to write an algorithm that sums up the values of ones with similar id.
My end result should be a new array with only the merged objects:
[
{id: 2, value: 12},
{id: 4, value: 3},
{id: 1, value: 15}
]
I've tried the following, but it doesn't work:
var arr = [];
arr.push({id: 2, visit:10});
arr.push({id: 4, visit:3});
arr.push({id: 2, visit:2});
arr.push({id: 1, visit:15});
// Deep copy
var copy = jQuery.extend(true, [], arr);
var masterArr = [];
for (var i = 0; i < arr.length; i++) {
var objArr = [];
objArr.push(arr[i]);
for (var j = copy.length-1; j > -1; j--) {
if (arr[i].id === copy[j].id) {
var q = copy.splice(j,1);
}
}
masterArr.push(objArr);
}
My plan was to first gather all similar objects in separate arrays (objArr), sum them up and put them in an end array (masterArr). I use jquerys extend to make a deep copy (not a reference) and reverse iteration and splice to remove objects thats already been found as "duplicates".
This doesn't work! And it doesn't seem to be a very efficient mehtod to solve my problem.
How could I do this? Performance isn't top priority but rather "nice to have"!
Thanks!
You can do it like this:
// Assuming:
a = [{id: 2, value: 10}, {id: 4, value: 3}, {id: 2, value: 2}, {id: 1, value: 15}]
var b = {}, // Temporary variable;
c = []; // This will contain the result;
// Build a id:value object ( {1: 15, 2: 12, 4: 3} )
a.map(function(current){b[current.id] = (b[current.id] || 0) + current.value});
for(var key in b){ // Form that into the desired output format.
c.push({id: parseInt(key, 10), value: b[key]});
}
console.log(c);
/* [{id: 1, value: 15},
{id: 2, value: 12},
{id: 4, value: 3}] */
I'm using parseInt(key, 10), since the keys are strings, you'll probably want them converted to integers again.
// First group the data based on id and sum the values
var temp = data.reduce(function(result, current) {
result[current.id] = (result[current.id] || 0) + current.value;
return result;
}, {});
// then recreate the objects with proper id and value properties
var result = [];
for (var key in temp) {
result.push({
id: parseInt(key, 10),
value: temp[key]
});
}
console.log(result);
Output
[ { id: 1, value: 15 },
{ id: 2, value: 12 },
{ id: 4, value: 3 } ]
The quickest approach loops over the array only once using Array.prototype.filter():
var tmp = {},
result = arr.filter(function (el) {
if (tmp.hasOwnProperty(el.id)) {
tmp[el.id].visit += el.visit;
return false;
}
else {
tmp[el.id] = el;
return true;
}
});
It also reuses the objects, though this renders the original array to contain inaccurate values. If this is a problem, you can modify the example to copy each object property to a new object.