The operation of the JavaScript array and object - javascript

oldList:
var oldList = [
{id:1, time:'2018-02-06 09:00-10:00', title:'aa'},
{id:2, time:'2018-02-06 11:00-12:00', title:'bb'},
{id:3, time:'2018-02-07 10:00:02', title:'cc'},
{id:4, time:'2018-02-07 09:00-10:00', title:'dd'}
];
console.log(oldList);
Desired:
var newList = [
{
'2018-02-06' : [
{id:1, time:'2018-02-06 09:00-10:00', title:'aa'},
{id:2, time:'2018-02-06 11:00-12:00', title:'bb'},
]
},
{
'2018-02-07' : [
{id:4, time:'2018-02-07 09:00-10:00', title:'dd'},
{id:3, time:'2018-02-07 10:00:02', title:'cc'},
]
},
];
console.log(newList);
How can I get the following result from this array and object?
I haven't found a good solution at the moment。

You can use reduce for this.
var oldList = [{
id: 1,
time: '2018-02-06 09:00-10:00',
title: 'aa'
},
{
id: 2,
time: '2018-02-06 11:00-12:00',
title: 'bb'
},
{
id: 3,
time: '2018-02-07 10:00:02',
title: 'cc'
},
{
id: 4,
time: '2018-02-07 09:00-10:00',
title: 'dd'
}
];
var newList = oldList.reduce(function(c, i) {
let t = i.time.split(" ")[0];
c[t] = c[t] || [];
c[t].push(i);
return c;
}, {});
console.log( newList );
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

you can use lodash to do this.
var newList = _.groupBy(oldList, function(o) {
return o.time.split(" ")[0];
});

var newList = {};
for (var i = 0; i < oldList.length; i++) {
var item = oldList[i];
var key = item.time; //here you can transform the key as you like (ie remove time)
if (newList[key] == null) {
newList[key] = [];
}
newList[key].push(item );
}
I created a dictionary. For each item in your old list I check if in the new list exist a key with your timestamp. If not, create a new entry with an empty array. Then, in both case, push your item into the specific array

Here's a solution without using reduce.
var oldList = [{
id: 1,
time: '2018-02-06 09:00-10:00',
title: 'aa'
}, {
id: 2,
time: '2018-02-06 11:00-12:00',
title: 'bb'
}, {
id: 3,
time: '2018-02-07 10:00:02',
title: 'cc'
}, {
id: 4,
time: '2018-02-07 09:00-10:00',
title: 'dd'
}];
var uniqueDates = []
for (i in oldList) {
if (uniqueDates.indexOf(oldList[i]['time'].split(' ')[0]) == -1) {
uniqueDates.push(oldList[i]['time'].split(' ')[0])
}
}
var newList = []
for (i in uniqueDates) {
var val = {}
val[uniqueDates[i]] = []
for (j in oldList) {
if(oldList[j]['time'].split(' ')[0] == uniqueDates[i]){
val[uniqueDates[i]].push(oldList[j])
}
}
newList.push(val)
}
console.log(newList)
But I like #Eddie's answer better

Related

I need to delete the object list by using matched key. (example the key have 1 and 2 , the result will only show 3)

let selectedRow = ["1","2","3"];
let arr = [
{ id:1, name:"eddie" },
{ id:2, name:"jake" },
{ id:3, name:"susan" },
];
Updation on the answer provided by Andy, If you don't want to update the exiting array and want to result in a new array
let selectedRow = ["1", "2"];
let arr = [
{ id: 1, name: "eddie" },
{ id: 2, name: "jake" },
{ id: 3, name: "susan" },
];
const result = arr.filter(item => !selectedRow.includes(item.id.toString()))
console.log(result)
If you want changes in a current array and don't want to store results in a new array (Not the most efficient solution though)
let selectedRow = ["1", "2"];
let arr = [
{ id: 1, name: "eddie" },
{ id: 2, name: "jake" },
{ id: 3, name: "susan" },
];
for (const row of selectedRow) {
const index = arr.findIndex(item => item.id.toString() === row)
if (index !== -1)
arr.splice(index, 1)
}
console.log(arr)
Make sure your selectedRow array is an array of numbers (because your object ids are numbers).
filter over the array of objects and only keep the ones that selectedRow doesn't include.
const arr = [{ id: 1, name: 'eddie' }, { id: 2, name: 'jake' }, { id: 3, name: 'susan' }];
const selectedRow = ['1', '2'].map(Number);
const result = arr.filter(obj => {
return !selectedRow.includes(obj.id);
});
console.log(result);

Javascript Array and Object

I have two arrays of objects.Where each object has different properties, Like this
let array1=[
{id:121122,name:"Matt Jeff"},
{id:121123,name:"Philip Jeff"},
{id:121124,name:"Paul Jeff"}]
let array2=[
{owner_id:121122,id:1211443,value:18},
{owner_id:121127,id:1211428,value:22}]
How can I check if the owner_id in the array2 is equal to the id in array1 then return the new array like this
let newArray=[
{owner_id:121122,id:1211443,value:18}
]
Where the owner_id in array2 is equal to the id in array1.
If I correctly understand what you need, you could do like this:
let array1 = [{
id: 121122,
name: "Matt Jeff"
}, {
id: 121123,
name: "Philip Jeff"
}, {
id: 121124,
name: "Paul Jeff"
}
]
let array2 = [{
owner_id: 121122,
id: 1211443,
value: 18
}, {
owner_id: 121127,
id: 1211428,
value: 22
}
]
const result = array2.filter(({ owner_id }) => array1.some(({ id }) => id === owner_id));
console.log(result);
You could try with nested for like:
let array1=[
{id:121122,name:"Matt Jeff"},
{id:121123,name:"Philip Jeff"},
{id:121124,name:"Paul Jeff"}]
let array2=[
{owner_id:121122,id:1211443,value:18},
{owner_id:121127,id:1211428,value:22}];
let result = [];
for(let i = 0; i < array1.length; i++) {
for(let j = 0; j < array2.length; j++) {
if (array1[i].id === array2[j].owner_id) {
result.push(array2[j]);
}
}
}
console.log(result)
EFFICIENT WAY: Using Set and filter
O(m) - Iterating on array1 and Storing the id in Set
O(n) - Iterating on the array2 and filtering the result which include O(1) to search in Set;
let array1 = [
{ id: 121122, name: "Matt Jeff" },
{ id: 121123, name: "Philip Jeff" },
{ id: 121124, name: "Paul Jeff" },
];
let array2 = [
{ owner_id: 121122, id: 1211443, value: 18 },
{ owner_id: 121127, id: 1211428, value: 22 },
];
const dict = new Set();
array1.forEach((o) => dict.add(o.id));
const result = array2.filter((o) => dict.has(o.owner_id));
console.log(result);

search in JSON tree with full structure intect

I take this has been asked before here filter nested tree object without losing structure
but what I am looking for is opposite of that.
For the JSON data
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}]
},
{
name: "b2",
id: 5,
children: [{
name: "a4",
id: 4
}]
}]
}
];
We want the filter so that if you search for a2. It should return the following
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}]
}]
}
];
i.e. all the nodes in that tree path (from root to leaf node).
Any idea how to achieve in nodejs/javascript?
Thanks
Follow my example, you can change the code to suit your works:
var items = [
{
name: "a1",
id: 1,
children: [{
name: "a2",
id: 2,
children: [{
name: "a3",
id: 3
}]
},
{
name: "b2",
id: 5,
children: [{
name: "a4",
id: 4
}]
}]
}
];
//console.log(items);
//first, add depth (of each element) to array items
var depths = items;
//structure of path = [[0,length_1st],[1,length_2nd],[2,length_3rd],[3,length_4th],...,[last,length_last]]
var path = [];
//for first value of path
path.push([0,depths.length]);
//test to add depth for depths:
depths.map(function add_depth(current){
current['depth'] = path[path.length-1][0];
if(current.children){
//continue to array children
path.push([path[path.length-1][0]+1,current.children.length]);
current.children.map(add_depth);
}else{
//get back of path
while(path.length>1 && path[path.length-1][1]<2){
path.pop();
}
//decrease length path[...[x,length]]
path[path.length-1][1]--;
};
});
//console.log(depths);
// has depth in array depths, now is function for search in array depths
function search_name(str){
let path_result = [];
let flagExit = false;
depths.findIndex(function find_name(current,index){
if (flagExit){
return;
};
if(current.name===str){
//finish at here
path_result[current.depth] = index;
flagExit = true;
return;
}else{
if(current.children){
path_result[current.depth] = index;
current.children.findIndex(find_name);
};
};
});
return path_result;
};
var name_to_search = "a3";
var path_end = search_name(name_to_search); //console.log(path_end);
//show result from path_end:
var result = [];
var self_items, self_result;
if (path_end){
for(let i=0;i<path_end.length;i++){
if(i===0){
result[i] = {};
result[i]['name'] = items[path_end[i]].name;
result[i]['id'] = items[path_end[i]].id;
if(i === path_end.length-1){
//just the first result
result[i]['children'] = items[path_end[i]].children;
}else{
result[i]['children'] = [];
self_items = items[path_end[i]].children;
self_result = result[i]['children'];
};
}else{
if(i !== path_end.length-1){
//not to the end
self_result[0] = {};
self_result[0]['name'] = self_items[path_end[i]].name;
self_result[0]['id'] = self_items[path_end[i]].id;
self_result[0]['children'] = [];
self_items = self_items[path_end[i]].children;
self_result = self_result[0]['children'];
}else{
//to the end, check for the children end
self_result[0] = {};
self_result[0]['name'] = self_items[path_end[i]].name;
self_result[0]['id'] = self_items[path_end[i]].id;
if(self_items[path_end[i]].children){
self_result[0]['chidren'] = self_items[path_end[i]].children;
};
//check again the searching, if not match the name_to_search, set result to empty!
if(self_result[0]['name'] !== name_to_search){
result = [];
}
};
}
};
}else{
result = [];
}
console.log(result);
The below solution uses object-scan.
Notes: (1) Input is not mutated (2) Well behaved input of form array -> children is expected.
// const objectScan = require('object-scan');
const finder = (name, input) => objectScan(['**(^children$).name'], {
abort: true,
useArraySelector: false,
filterFn: ({ key, value, context, parents }) => {
if (value !== name) {
return false;
}
let cur = context;
for (let idx = 0; idx < key.length - 1; idx += 1) {
const segment = key[idx];
if (idx % 2 === 0) {
cur.push({ ...parents[parents.length - 1 - idx][segment] });
cur = cur[0];
} else {
cur[segment] = [];
cur = cur[segment];
}
}
return true;
}
})(input, []);
const items = [{ name: 'a1', id: 1, children: [{ name: 'a2', id: 2, children: [{ name: 'a3', id: 3 }] }, { name: 'b2', id: 5, children: [{ name: 'a4', id: 4 }] }] }];
console.log(finder('a2', items));
// => [ { name: 'a1', id: 1, children: [ { name: 'a2', id: 2, children: [ { name: 'a3', id: 3 } ] } ] } ]
.as-console-wrapper {max-height: 100% !important; top: 0}
<script src="https://bundle.run/object-scan#13.8.0"></script>
Disclaimer: I'm the author of object-scan

merge array with unique keys

I have array of objects called newArray and oldArray.
Like this : [{name: 'abc', label: 'abclabel', values: [1,2,3,4,5]}]
example :
newArray = [
{name: 'abc', label: 'abclabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4]}
]
oldArray = [
{name: 'oldArray', label: 'oldArrayLabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4,5]}
]
result will be = [
{name: 'abc', label: 'abclabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4]},
{name: 'oldArray', label: 'oldArrayLabel', values: [1,2,3,4,5]}
];
I wanted to merge both the array in such a way that whenever name and label are equal in both the arrays it should only consider newArray value.
I have tried
function mergeArrayWithLatestData (newData, oldData) {
let arr = [];
let i = 0; let j =0
while ((i < newData.length) && (j < oldData.length)) {
if ((findIndex(newData, { name: oldData[i].name, label: oldData[i].label })) !== -1) {
arr.push(newData[i])
} else {
arr.push(newData[i]);
arr.push(oldData[i]);
}
i += 1;
j += 1;
}
while (i < newData.length) {
arr.push(newData[i]);
}
return arr;
}
But i am not getting correct result.
Any suggestions?
You could add all array with a check if name/label pairs have been inserted before with a Set.
var newArray = [{ name: 'abc', label: 'abclabel', values: [1, 2, 3, 4, 5] }, { name: 'test', label: 'testlabel', values: [1, 2, 3, 4] }],
oldArray = [{ name: 'oldArray', label: 'oldArrayLabel', values: [1, 2, 3, 4, 5] }, { name: 'test', label: 'testlabel', values: [1, 2, 3, 4, 5] }],
result = [newArray, oldArray].reduce((s => (r, a) => {
a.forEach(o => {
var key = [o.name, o.label].join('|');
if (!s.has(key)) {
r.push(o);
s.add(key);
}
});
return r;
})(new Set), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can simply use Array.reduce() to create a map of the old Array and group by combination of name and label. Than iterate over all the elements or objects of the new Array and check if the map contains an entry with given key(combination of name and label), if it contains than simply update it values with the values of new array object, else add it to the map. Object.values() on the map will give you the desired result.
let newArray = [ {name: 'abc', label: 'abclabel', values: [1,2,3,4,5]}, {name: 'test', label: 'testlabel', values: [1,2,3,4]} ];
let oldArray = [ {name: 'oldArray', label: 'oldArrayLabel', values: [1,2,3,4,5]}, {name: 'test', label: 'testlabel', values: [1,2,3,4,5]} ];
let map = oldArray.reduce((a,curr)=>{
a[curr.name +"_" + curr.label] = curr;
return a;
},{});
newArray.forEach((o)=> {
if(map[o.name +"_" + o.label])
map[o.name +"_" + o.label].values = o.values;
else
map[o.name +"_" + o.label] = o;
});
console.log(Object.values(map));
In your first while loop
while ((i < newData.length) && (j < oldData.length)) {
if ((findIndex(newData, { name: oldData[i].name, label: oldData[i].label })) !== -1)
{
arr.push(newData[i])
} else {
arr.push(newData[i]);
arr.push(oldData[i]);
}
i += 1;
j += 1;
}
i and j always have the same value, you are only comparing entries at the same positions in the arrays. If they have different lengths, you stop comparing after the shorter array ends. Your second while-loop will only be executed if newArray is larger than oldArray.
One possible solution is to copy the oldArray, then iterate over newArray and check if the same value exists.
function mergeArrayWithLatestData (newData, oldData) {
let arr = oldData;
for(let i = 0; i < newData.length; i++) {
let exists = false;
for(let j = 0; j < oldData.length; j++) {
if(newData[i].name === oldData[j].name && newData[i].label === oldData[j].label) {
exists = true;
arr[j] = newData[i];
}
}
if(!exists) {
arr.push(newData[i]);
}
}
return arr;
}
var newArray = [
{name: 'abc', label: 'abclabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4]}
]
var oldArray = [
{name: 'oldArray', label: 'oldArrayLabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4,5]}
]
console.log(mergeArrayWithLatestData(newArray, oldArray));
You make copies of the original arrays, and in the first one, or change the element, or add:
function mergeArrayWithLatestData (a1, a2) {
var out = JSON.parse(JSON.stringify(a1))
var a2copy = JSON.parse(JSON.stringify(a2))
a2copy.forEach(function(ae) {
var i = out.findIndex(function(e) {
return ae.name === e.name && ae.label === e.label
})
if (i!== -1) {
out[i] = ae
} else {
out.push(ae)
}
})
return out
}
[ https://jsfiddle.net/yps8uvf3/ ]
This is Using a classic filter() and comparing the name/label storing the different pairs using just +. Using destructuring assignment we merge the two arrays keeping the newest first, so when we check the different the newest is always the remaining.
var newArray = [{ name: "abc", label: "abclabel", values: [1, 2, 3, 4, 5] },{ name: "test", label: "testlabel", values: [1, 2, 3, 4] }];
var oldArray = [{ name: "oldArray", label: "oldArrayLabel", values: [1, 2, 3, 4, 5] },{ name: "test", label: "testlabel", values: [1, 2, 3, 4, 5] }];
var diff = [];
oldArray = [...newArray, ...oldArray].filter(e => {
if (diff.indexOf(e.name + e.label) == -1) {
diff.push(e.name + e.label);
return true;
} else {
return false; //<--already exist in new Array (the newest)
}
});
console.log(oldArray);
Create an object, with key as name and label. Now, first add all the oldData records to the object and then add newData records in object. If there are any objects in common with same name and label, it will overwrite the old Data value. Finally, get the values of the Object which is the merged data set.
var arr1 = [{name: 'def', label: 'abclabel', values: [6,7]}, {name: 'abc', label: 'abclabel', values: [1,2,3,4,5]}];
var arr2 = [{name: 'xy', label: 'abclabel', values: [6,7]}, {name: 'abc', label: 'abclabel', values: [6,7]}];
function mergeArrayWithLatestData(newData, oldData) {
var result = {};
[...oldData, ...newData].forEach(o => result[o.name + "~~$$^^" + o.label] = o);
return Object.values(result);
}
let result = mergeArrayWithLatestData(arr1, arr2);
console.log(result);
Alternative: using a Map as the initial value in a reducer. You should know that (as in the selected answer) you loose information here, because you're not comparing on the values property within the array elements. So one of the objects with name/label pair test/testlabel will be lost in the merged Array. If concatenation in the snippet was the other way around (so newArray.concat(oldArray), the test/testLabel Object within the merged Array would contain another values property value.
const newArray = [
{name: 'abc', label: 'abclabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4]}
];
const oldArray = [
{name: 'oldArray', label: 'oldArrayLabel', values: [1,2,3,4,5]},
{name: 'test', label: 'testlabel', values: [1,2,3,4,5]}
];
const merged = [
...oldArray.concat(newArray)
.reduce( (map, value) =>
map.set(`${value.name}${value.label}`, value),
new Map())
.values()
];
console.log(merged);
function mergeArray(newArray, oldArray) {
var tempArray = newArray;
oldArray.forEach(oldData => {
var isExist = tempArray.findIndex(function (newData) {
return oldData.name === newData.name;
});
if (isExist == -1) {
tempArray.push(oldData);
}
});
return tempArray;
}
var newArray = [{
name: 'abc',
label: 'abclabel',
values: [1, 2, 3, 4, 5]
}, {
name: 'test',
label: 'testlabel',
values: [1, 2, 3, 4]
}];
var oldArray = [{
name: 'oldArray',
label: 'oldArrayLabel',
values: [1, 2, 3, 4, 5]
}, {
name: 'test',
label: 'testlabel',
values: [1, 2, 3, 4, 5]
}];
var resultArray = [];
resultArray = mergeArray(newArray, oldArray);
console.log(resultArray);

Create object from a property in an array of objects

I have an array of objects like this:
[
{id: 'id1', random: 'labels.prop1'},
{id: 'id2', random: 'labels.prop2'},
{id: 'id3', random: 'texts.anotherprop'}
]
Is there a short way to generate from that array an object based on the property random? I'd need this:
{
texts: {
anotherprop: ''
},
labels: {
prop1: '',
prop2: ''
}
}
You can use reduce() two times to build this nested object.
var data = [
{id: 'id1', random: 'labels.prop1'},
{id: 'id2', random: 'labels.prop2'},
{id: 'id3', random: 'texts.anotherprop'}
]
var result = data.reduce(function(r, o) {
var arr = o.random.split('.')
arr.reduce(function(a, b, i) {
return (i != arr.length - 1) ? a[b] || (a[b] = {}) : a[b] = ''
}, r)
return r;
}, {})
console.log(result)
You could iterate the array and build an object based on the parts of random.
function setValue(object, path, value) {
var fullPath = path.split('.'),
way = fullPath.slice(),
last = way.pop();
way.reduce(function (r, a) {
return r[a] = r[a] || {};
}, object)[last] = value;
}
var data = [{ id: 'id1', random: 'labels.prop1' }, { id: 'id2', random: 'labels.prop2' }, { id: 'id3', random: 'texts.anotherprop' }],
result = {};
data.forEach(function (o) {
setValue(result, o.random, '');
});
console.log(result);
var arr = [
{id: 'id1', random: 'labels.prop1'},
{id: 'id2', random: 'labels.prop2'},
{id: 'id3', random: 'texts.anotherprop'}
];
var result = {};
arr.forEach(function(o) {
var parts = o.random.split('.');
var r = result;
var i = 0;
for( ; i < parts.length - 1; i++) {
r[parts[i]] = r[parts[i]] || {};
r = r[parts[i]];
}
r[parts[i]] = '';
});
console.log(result);

Categories