I have two objects:
let a = [{id: 1, selected: false, key: "plan"}];
let b = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
I need to merge them and get:
let c = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
My main purpose to rewrite default object on modified
I have tried:
let c = {...a, ...b};
You can use reduce in order to replace the data that is being fetched from server.
Below I have simulated different scenarios considering a as the original array and b & c as the responses from the server
let a = [{ id: 1, selected: false, key: 'plan' }];
let b = [
{ id: 1, selected: true, key: 'plan', text: 'aaaa' },
{ id: 2, selected: true },
];
const mergeArrays = (array1, array2) => {
array2 = array2 || [];
array1 = array1 || [];
return Object.values([...array1, ...array2].reduce((result, obj) => {
result = {
...result,
[obj.id]: {...obj}
}
return result;
}, {}))
};
//Consider you got the response from server
//a -> Original array, b -> Response from serer
console.log(mergeArrays(a, b))
//Response from server is null or []
console.log(mergeArrays(a, null))
//original array is empty but there's a response from server
console.log(mergeArrays([], b))
//Consider there is completely a different object in the array received from the server than the one defined in the original array
let c = [{ id: 2, selected: true, key: 'plan' }];
console.log(mergeArrays(a, c))
.as-console-wrapper {
max-height: 100% !important;
}
Hope this helps.
you must first determine which one is biggest then, if b is bigger than a:
let a = [{id: 1, selected: false, key: "plan"}];
let b = [{id: 1, selected: true, key: "plan", "text": "aaaa"}, {id: 2, selected: true}];
let bigger = a.length > b.length ? a : b;
let shorter = a.length < b.length ? a : b;
console.log(bigger.map(x => ({...x, ...shorter.find(y => y.id === x.id)})))
You can use destructure:
let c = [...a,...b];
Related
I have an array of objects. Each object has a key quantity and value. I want to duplicate each object in the array based on its quantity. Next, I want to manipulate only one of the duplicated object in the array. But on manipulating value of 1 object, value of all duplicated objects change. Here is my code:
let arr = [
{ id: 1, quantity: 3, value: 10 },
{ id: 2, quantity: 1, value: 5 },
{ id: 2, quantity: 5, value: 5 },
];
const newArr = [];
for (const a of arr) {
if (a.quantity > 1) {
let quantity = a.quantity;
a.quantity = 1;
while (quantity--) {
newArr.push(a);
}
}
}
arr = newArr;
arr[0].value = 1;
When I changed the value of arr[0] to 1, value field of arr[1] and arr[2] also changed to 1.
I have tried copying the object using spread operator and JSON.parse(JSON.parse()), but none has worked.
Because newArr.push(a) .a push to newArr ref to element of arr
You can edit same as :
let arr = [
{ id: 1, quantity: 3, value: 10 },
{ id: 2, quantity: 1, value: 5 },
{ id: 2, quantity: 5, value: 5 },
]
const newArr = []
for (const a of arr) {
if (a.quantity > 1) {
let quantity = a.quantity;
a.quantity = 1;
while (quantity--) {
newArr.push({...a})
}
}
}
arr = [...newArr]
arr[0].value = 1
console.log(arr)
// example for Memory Management
let a = { id: 1, quantity: 3, value: 10 }
let b = { id: 1, quantity: 3, value: 10 }
let c = arr[0]
let d = {...arr[0]}
console.log(a === arr[0]) // false : different allocates memory for contain value
console.log(a === b) // false : different allocates memory for contain value
console.log(c === arr[0]) // true : refer to a memory
console.log(d === arr[0]) // false : different allocates memory for contain value
interface FormValues {
key: string;
value: any;
}
const array: FormValues[] = [
{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 'str' // string
},
{
key: 'C',
value: { a: 1, b: '2' } // object
},
{
key: 'C',
value: ['a','2'] // array
},
{
key: 'C',
value: ['a','2'] // array
}
{
key: 'B',
value: true // boolean
}
]
I want to filter the objects based on field value, which can have a value of any type.
I tried to do it like this; my solution is not working for nested object checks.
const key = 'value';
const arrayUniqueByKey = [...new Map(array.map(item => [item[key], item])).values()];
output :
[{
key: 'A',
value: 1 // number
},
{
key: 'A',
value: 'str' // string
},
{
key: 'C',
value: { a: 1, b: '2' } // object
},
{
key: 'C',
value: ['a','2'] // array
},
{
key: 'B',
value: true // boolean
}]
You need to decide what makes two distinct objects "equal". In JavaScript, all built-in comparisons of objects (which includes arrays) are by reference. That means ['a','2'] === ['a','2'] is false because two distinct array objects exist, despite having the same contents. See How to determine equality for two JavaScript objects? for more information.
I will take the approach that you would like two values to be considered equal if they serialize to the same value via a modified version of JSON.stringify() where the order of property keys are guaranteed to be the same (so {a: 1, b: 2} and {b: 2, a: 1} will be equal no matter how those are stringified). I use a version from this answer to do so:
function JSONstringifyOrder(obj: any, space?: number) {
var allKeys: string[] = [];
var seen: Record<string, null | undefined> = {};
JSON.stringify(obj, function (key, value) {
if (!(key in seen)) {
allKeys.push(key); seen[key] = null;
}
return value;
});
allKeys.sort();
return JSON.stringify(obj, allKeys, space);
}
And now I can use that to make the keys of your Map:
const arrayUniqueByKey = [...new Map(array.map(
item => [JSONstringifyOrder(item[key]), item]
)).values()];
And you can verify that it behaves as you'd like:
console.log(arrayUniqueByKey);
/* [{
"key": "A",
"value": 1
}, {
"key": "A",
"value": "str"
}, {
"key": "C",
"value": {
"a": 1,
"b": "2"
}
}, {
"key": "C",
"value": [
"a",
"2"
]
}, {
"key": "B",
"value": true
}] */
Playground link to code
This will combine any duplicate keys, creating a new property values to hold the array of combined values (from like keys).
const array = [{key: 'A', value: 1},{key: 'A', value: 'str'},{key: 'C', value: { a: 1, b: '2'}},{key: 'B',value: true}]
const arrayUniqueByKey = [array.reduce((b, a) => {
let f = b.findIndex(c => c.key === a.key)
if (f === -1) return [...b, a];
else {
b[f].values = [...[b[f].value], a.value];
return b
}
}, [])];
console.log(arrayUniqueByKey)
You can use Array.prototype.reduce() combined with JSON.stringify() and finaly get the result array of values with Object.values()
const array = [{key: 'A',value: 1,},{key: 'A',value: 1,},{key: 'A',value: 'str',},{key: 'C',value: { a: 1, b: '2' },},{key: 'C',value: ['a', '2'],},{key: 'C',value: ['a', '2'],},{key: 'B',value: true}]
const result = Object.values(array.reduce((a, c) => ((a[JSON.stringify(c)] = c), a), {}))
console.log(result)
I tried adding an id property to the objects in my sorted output, but all I'm doing is not working. Is there anything I should have done?
My Code Below:
var arr = [{ one: 2 },
{ two: 3 },
{ three: 4 },
{ four: 1 }];
const arr1 = arr.reduce((a,b) => ({...a,...b}), {})
var sorting = Object.entries(arr1).sort((a, b) => b[1] - a[1]);
console.log(sorting);
Expected Result:
var arr1 = [{ name: "three", value: 4, id: 1 },
{ name: "two", value: 3, id: 2 },
{ name: "one", value: 2, id: 3 },
{ name: "four", value: 1, id: 4 }];
Object.assign can do what you did with reduce, and I would not call that result arr1, as it is not an array, but a plain object.
In the final step it helps to use destructuring and map to an object literal with shortcut notation:
const arr = [{one: 2}, {two: 3}, {three: 4}, {four: 1}];
const obj = Object.assign({}, ...arr);
const sorted = Object.entries(obj).sort((a, b) => b[1] - a[1]);
const result = sorted.map(([text, value], i) => ({ text, value, id: i+1}));
console.log(result);
/*If i console.log(sorting) I have
[['three', 4 ], ['two', 3 ], ['one', 2 ], ['four', 1 ],]
Without Ids but i want something like the expected result below*/
/* Expected Result
[['three', 4 id = 1], ['two', 3 id = 2], ['one', 2 id = 3], ['four', 1 id = 4],]
*/
UPD, sorry, didn't get it right first time
var empty = [];
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
},{
four: 1
}];
const arr1 = arr.reduce((a,b) => ({...a,...b}), {})
const sorting = Object.entries(arr1).sort((a, b) => b[1] - a[1]);
// Add indexes starting from 1
const indexed = sorting.map((a,b) => a.push({ "id": b+1 }));
console.log(sorting);
I am going to go with a guess here that you want a descending sorted array of objects, adding an id property based on the original index + 1 of each original object. We can do that by reference to the object key (first property 0) when we sort after we add the ids to the original objects in a new array.
// not used in the question/issue
//var empty = [];
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
}, {
four: 1
}];
const newArr = [];
arr.forEach(function(currentValue, index, arr) {
currentValue.id = index + 1;
newArr.push(currentValue);
}, arr);
//console.log("arrObj:", newArr);
var sorted = newArr.sort(function(a, b) {
return b[Object.keys(b)[0]] - a[Object.keys(a)[0]];
});
console.log("sorted:", sorted);
EDIT: new based on comment
var arr = [{
one: 2
}, {
two: 3
}, {
three: 4
}, {
four: 1
}];
const newArr = [];
arr.forEach(function(currentValue, index, arr) {
let newValue = {};
newValue.text = Object.keys(currentValue)[0];
newValue.Value = currentValue[Object.keys(currentValue)[0]];
newValue.id = index + 1;
newArr.push(newValue);
}, arr);
//console.log("arrObj:", newArr);
var sorted = newArr.sort(function(a, b) {
return b.Value - a.Value;
});
console.log("sorted:", sorted);
output of this last is
sorted: [
{
"text": "three",
"Value": 4,
"id": 3
},
{
"text": "two",
"Value": 3,
"id": 2
},
{
"text": "one",
"Value": 2,
"id": 1
},
{
"text": "four",
"Value": 1,
"id": 4
}
]
My JSON array
var jData = [
{id: 1, parent: null},
{id: 2, parent: null},
{id: 3, parent: 1},
{id: 4, parent: 2},
{id: 5, parent: 2},
{id: 6, parent: 1}];
I want this be to sorted like the following ( by id then by the parent )
[
{id: 1, parent: null},
{id: 3, parent: 1},
{id: 6, parent: 1}
{id: 2, parent: null},
{id: 4, parent: 2},
{id: 5, parent: 2},
];
What is the best way to do it in JavaScript?
I tried, but no luck
jData .sort((a, b) => a.id - b.id ||a.parent- b.parent);
Help!!
You need a topological sorting first and then take the nodes in order of appearance.
function getData(array) {
return array.flatMap(({ data, children = [] }) => [data, ...getData(children)]);
}
var data = [{ id: 1, parent: null }, { id: 2, parent: null }, { id: 3, parent: 1 }, { id: 4, parent: 2 }, { id: 5, parent: 2 }, { id: 6, parent: 1 }],
tree = function (data, root) {
var t = {};
data.forEach(data => {
Object.assign(t[data.id] = t[data.id] || {}, { data });
t[data.parent] = t[data.parent] || {};
t[data.parent].children = t[data.parent].children || [];
t[data.parent].children.push(t[data.id]);
});
return t[root].children;
}(data, null),
result = getData(tree);
console.log(result);
console.log(tree); // just to show what's happening
.as-console-wrapper { max-height: 100% !important; top: 0; }
If parent is null we use the id as parent value and sort by parent first (otherwise we won't reach your result). If the parent value comparison results in zero, we sort by id.
var jData = [{id:5,parent:2},{id:1,parent:null},{id:4,parent:2},{id:2,parent:null},{id:3,parent:1},{id:6,parent:1}];
let res = jData.sort((a,b) => {
let ap = a.parent ? a.parent : a.id,
bp = b.parent ? b.parent : b.id;
return ap - bp || a.id - b.id;
});
console.log(res);
You can use reduce to group each array to its parent. Use 0 if parent is null. Use another reduce to contruct the final array.
var jData = [{"id":1,"parent":null},{"id":2,"parent":null},{"id":3,"parent":1},{"id":4,"parent":2},{"id":5,"parent":2},{"id":6,"parent":1}]
var temp = jData.reduce((c, v) => {
let p = v.parent || 0;
c[p] = c[p] || [];
c[p].push(v);
return c;
}, {});
var newjData = temp[0].reduce((c, v) => {
var o = temp[v.id] || [];
o.sort((a, b) => a.id - b.id); //Sort. Incase the IDs are not in order in the original array.
c.push(v, ...o);
return c;
}, []);
console.log(newjData);
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);