I have this data set in object, keys are strings
{ '17': '17-17:30',
'20': '20:00-21',
'21': '21-22',
'22': '22-23',
'23': '23-24',
'01': '1-2',
'02': '2-3',
'03': '3-4',
'04': '4-5',
'05': '5-6',
'06': '6-7',
'07': '7-7:30',
'08': '08:50-9' }
I want to arrange them numerically so that 01 comes first and 23 comes last.
Here's the code I'm using:
var sort_object = function(map) {
var keys = _.sortBy(_.keys(map), function(a) { return Number(a); });
var newmap = {};
_.each(keys, function(k) {
newmap[k] = map[k];
});
return newmap;
}
it still returns 17 in the beginning.
Where did I go wrong?
As Blender pointed out, objects are not ordered. You need to use an array. There are several ways you could set up this array, and once you have the data in an array it's very easy to sort it.
Here are a couple of examples. First, let's try making this an array of arrays. The outer array is the list of elements, and each element of this array is itself an array of two elements, your key and your value.
So paste this code into your favorite JavaScript console, such as the one in Chrome:
var data = [
[ '17', '17-17:30' ],
[ '20', '20:00-21' ],
[ '21', '21-22' ],
[ '22', '22-23' ],
[ '23', '23-24' ],
[ '01', '1-2' ],
[ '02', '2-3' ],
[ '03', '3-4' ],
[ '04', '4-5' ],
[ '05', '5-6' ],
[ '06', '6-7' ],
[ '07', '7-7:30' ],
[ '08', '08:50-9' ]
];
var result = data.slice().sort( function( a, b ) {
return +a[0] - +b[0];
});
JSON.stringify( result, null, 4 );
It will log:
[
[
"01",
"1-2"
],
[
"02",
"2-3"
],
[
"03",
"3-4"
],
[
"04",
"4-5"
],
[
"05",
"5-6"
],
[
"06",
"6-7"
],
[
"07",
"7-7:30"
],
[
"08",
"08:50-9"
],
[
"17",
"17-17:30"
],
[
"20",
"20:00-21"
],
[
"21",
"21-22"
],
[
"22",
"22-23"
],
[
"23",
"23-24"
]
Or, instead of an array of arrays, you can use an array of objects. This is often more convenient to work with. Paste this code into the JavaScript console:
var data = [
{ key: '17', value: '17-17:30' },
{ key: '20', value: '20:00-21' },
{ key: '21', value: '21-22' },
{ key: '22', value: '22-23' },
{ key: '23', value: '23-24' },
{ key: '01', value: '1-2' },
{ key: '02', value: '2-3' },
{ key: '03', value: '3-4' },
{ key: '04', value: '4-5' },
{ key: '05', value: '5-6' },
{ key: '06', value: '6-7' },
{ key: '07', value: '7-7:30' },
{ key: '08', value: '08:50-9'}
];
var result = data.slice().sort( function( a, b ) {
return +a.key - +b.key;
});
JSON.stringify( result, null, 4 );
It will log:
[
{
"key": "01",
"value": "1-2"
},
{
"key": "02",
"value": "2-3"
},
{
"key": "03",
"value": "3-4"
},
{
"key": "04",
"value": "4-5"
},
{
"key": "05",
"value": "5-6"
},
{
"key": "06",
"value": "6-7"
},
{
"key": "07",
"value": "7-7:30"
},
{
"key": "08",
"value": "08:50-9"
},
{
"key": "17",
"value": "17-17:30"
},
{
"key": "20",
"value": "20:00-21"
},
{
"key": "21",
"value": "21-22"
},
{
"key": "22",
"value": "22-23"
},
{
"key": "23",
"value": "23-24"
}
]
Either way, as you can see, once you have your data in a suitable format (i.e. the data as a whole needs to be an array), then it becomes very simple to sort it.
Let's look at that sort function in more detail (using the second example):
var result = data.slice().sort( function( a, b ) {
return +a.key - +b.key;
});
In this code a.key gets the value for the key property of one of the array elements. The + in front of that converts the string to a number - much like using the Number function but a simpler way to do it. Similarly for +b.key. And then subtracting those two numbers gives the correct sort function return: a positive, negative, or zero value depending on whether a.key or b.key is the greater of the two or if they're equal.
The first example works similarly, just using +a[0] - +b[0] instead of +a.key - +b.key.
The .slice() call in each example makes a copy of the array. You could omit that if you want to sort the original array.
The only way to imply order would be to use an array. How about an array of key/value objects, eg
return Object.keys(map).sort().map(function(k) {
return {key: k, value: foo[k]}
});
Related
I have 2 arrays containing objects, I want to create a new array without "duplicates". The problem is that the two arrays are not identical and I want to choose based on id.
This is a simple version of what I got:
let array1 = [
{
"id": "1",
"type": "abc"
},
{
"id": "2",
"type": "one, two, three"
}
]
let array2 = [
{
"id": "1",
"type": "abc",
"country": "England"
},
{
"id": "3",
"type": "one, two, three",
"country": "Germany"
}
]
let array3 = array1.filter(x => array2.forEach(y => x.id === y.id)).concat(array2);
The result I want for array3 is:
{
"id": "1",
"type": "abc"
}
In this particular case country is not important and will be thrown away anyway.
How do I filter out unwanted objects and create a new array with only the objects I want?
If you are looking for an intersection between the arrays the shortest answer is to use the filter with a find.
let array1 = [{
id: '1',
type: 'abc',
},
{
id: '2',
type: 'one, two, three',
},
];
let array2 = [{
id: '1',
type: 'abc',
country: 'England',
},
{
id: '3',
type: 'one, two, three',
country: 'Germany',
},
];
const array3 = array1.filter(value => array2.find((a)=>a.id==value.id));
console.log(array3);
let array1 = [{
id: '1',
type: 'abc',
},
{
id: '2',
type: 'one, two, three',
},
];
let array2 = [{
id: '1',
type: 'abc',
country: 'England',
},
{
id: '3',
type: 'one, two, three',
country: 'Germany',
},
];
array2.forEach((el, idx) => {
const match = array1.find((el2) => el2.id === el.id);
if (!match) array1.push(el);
});
console.log(array1);
Something like this ? I assumed from your explanation that you want to merge array2 with array1.
You will end up with the unique objects from array2 having the extra country property, if you want to delete that you need an extra step before pushing.
I think this is what you were trying to achieve:
let array1 = [ { "id": "1", "type": "abc" }, { "id": "2", "type": "one, two, three" } ];
let array2 = [ { "id": "1", "type": "abc", "country": "England" }, { "id": "3", "type": "one, two, three", "country": "Germany" } ];
let array3 = array1.filter(x => array2.map(y => y.id).includes(x.id));
console.log( array3 );
In order to avoid have to recompute the array2 array of ids you can get them in advance as follows:
let array1 = [ { "id": "1", "type": "abc" }, { "id": "2", "type": "one, two, three" } ];
let array2 = [ { "id": "1", "type": "abc", "country": "England" }, { "id": "3", "type": "one, two, three", "country": "Germany" } ];
const ids = array2.map(({id}) => id);
let array3 = array1.filter(x => ids.includes(x.id));
console.log( array3 );
I have the following array of arrays
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
I'd like to achieve this structure
let foo = [
{
value: "Female",
children: [
{
value: "Dinner",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
{
value: "Lunch",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
]
},
{
value: "Male",
children: [
{
value: "Dinner",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
{
value: "Lunch",
children: [
{
value: "No"
},
{
value: "Yes"
},
]
},
]
},
]
I simply can't wrap my head around the problem to achieve this, thus, I don't have a starting code to post, so please if you can help, it would be great.
recursion
Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutation, variable reassignments, and other side effects.
We can write make(t) using inductive inductive reasoning -
If the input t is empty, return the empty result []
(inductive) t has at least one element. For all value in the first element t[0], return a new object {value, children} where children is the result of the recursive sub-problem make(t.slice(1))
const make = t =>
t.length == 0
? [] // 1
: t[0].map(value => ({ value, children: make(t.slice(1)) })) // 2
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
Above we write make as a single pure functional expression using ?:. This is equivalent to an imperative style if..else -
function make(t) {
if (t.length == 0)
return []
else
return t[0].map(value => ({ value, children: make(t.slice(1)) }))
}
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
visualize
It helps for us to visualize how these work
make([[ "Female" , "Male" ], [ "Dinner" , "Lunch" ], [ "No" , "Yes" ]])
= [
{value: "Female", children: make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]]) },
{value: "Male", children: make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]]) }
]
make([[ "Dinner" , "Lunch" ], [ "No" , "Yes" ]])
= [
{value: "Dinner", children: make([[ "No" , "Yes" ]]) },
{value: "Lunch", children: make([[ "No" , "Yes" ]]) }
}
make([[ "No" , "Yes" ]])
= [
{value: "No", children: make([]) },
{value: "Yes", children: make([]) }
}
make([])
= []
remove empty children
Now that we see how it works, we prevent making empty children: [] properties by adding one more conditional. When t has just one element, simply create a {value} for all value in the element -
function make(t) {
switch (t.length) {
case 0:
return []
case 1:
return t[0].map(value => ({ value }))
default:
return t[0].map(value => ({ value, children: make(t.slice(1)) }))
}
}
const myinput = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ]
]
console.log(make(myinput))
Which produces the output you are looking for -
[
{
"value": "Female",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
},
{
"value": "Male",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
}
]
You can also do it without recursion with 2 for
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
];
var lastChild = -1;
for(var i = arr.length-1; i >= 0; i--) {
var item = arr[i];
var lastChildTemp = [];
for(var j = 0; j < item.length; j++) {
var newChild = {value: item[j]};
if(lastChild != -1) {
newChild.children = lastChild;
}
lastChildTemp.push(newChild);
}
lastChild = lastChildTemp;
}
console.log(JSON.stringify(lastChildTemp,null,2));
Output:
[
{
"value": "Female",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
},
{
"value": "Male",
"children": [
{
"value": "Dinner",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
},
{
"value": "Lunch",
"children": [
{
"value": "No"
},
{
"value": "Yes"
}
]
}
]
}
]
The key here is to use backward for (starting from high index to low index), then create a lastChild object. Then put it in .children attribute of each next objects.
Rearrange your Array using the below code, then iterate as your wish and this is dynamic. you can have more rows in arr variable.
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
for(let i=arr.length-2; i>-1; i--){
for(let j=0; j< arr[i].length; j++) {
item = {}
item[arr[i][j]] = arr[i+1];
arr[i][j] = [];
arr[i][j] = item;
}
arr.pop();
}
console.log(arr);
/*output*/
[
[{
'Female': [{
'Dinner': ['No', 'Yes']
}, {
'Lunch': ['No', 'Yes']
}]
}, {
'Male': [{
'Dinner': ['No', 'Yes']
}, {
'Lunch': ['No', 'Yes']
}]
}]
]
https://jsfiddle.net/Frangly/ywsL0pbt/149/
You can try this:
let arr = [
['Female', 'Male'],
['Dinner', 'Lunch'],
['No', 'Yes']
]
function makeTree(a, ch = [], currIndex = 0) {
for (const item of a[currIndex]) {
if (a[currIndex + 1]) {
// If there is an array after this one then
// include the 'children' array
const obj = { value: item, children: [] }
ch.push(obj)
// Run the function again to fill the `children`
// array with the values of the next array
makeTree(a, obj.children, currIndex + 1)
} else {
// If this is the last array then
// just include the value
ch.push({ value: item })
}
}
return ch
}
const result = makeTree(arr)
console.log(JSON.stringify(result, null, 2))
.as-console-wrapper { min-height: 100% }
Checkout this code snippet. It outputs as per your need.
let arr = [
[ "Female" , "Male" ],
[ "Dinner" , "Lunch" ],
[ "No" , "Yes" ],
]
let foo = [];
let arr2 = [];
arr[2].forEach(yn => {
arr2.push({ "value": yn});
});
let arr1 = [];
arr[1].forEach(dl => {
arr1.push({
"value": dl,
"children": arr2
});
});
arr[0].forEach(fm => {
foo.push({
"value": fm,
"children": arr1
});
});
console.log(JSON.stringify(foo, null, 2))
I am working in small react project & I am facing issue in grouping the data. Requirement is to group the id & its feature into a single row if same id is there in before & after object.
Json Data:
{
"before":{
"device": [
{
id:"1234",
price:"10,
features:[
{name:"samsung",price:"10"},
{name:"Apple",price:"20"}
]
},
{id:"2154",
price:"20,
features:[
{name:"samsung",price:"30"},
{name:"Moto",price:"40"}
]
]
},
"after":{
"device": [
{
id:"1234",
price:"50,
features:[
{name:"samsung",price:"20"},
{name:"Lenovo",price:"30"}
]
},
{id:"2158",
price:"40,
features:[
{name:"samsung",price:"30"}
]
]
}
}
Expected grouping to be shown in UI is shared in image.
I tried to get unique ids in one array and lopping through after array and comparing unique array id I am getting unique id to show but issue i am facing while grouping their related feature.
Can anyone please help me to get a best approach to handle this requirement.
Thanks
There are 3 things i'd suggest you:
1.) Please verify the data your'e posting is correct and in proper format, people won't be able to help if the data is incorrect.
2.) The UI display requirement should be simple enough.
Now, if you still want to achieve this requirement i believe the correct JSON and the merged output json will look something like below:
//Correct input data that you have:
var input = {
"before": {
"device": [
{
"id": "1234",
"price": "10",
"features": [
{
"name": "samsung",
"price": "10"
},
{
"name": "Apple",
"price": "20"
}
]
},
{
"id": "2154",
"price": "20",
"features": [
{
"name": "samsung",
"price": "30"
},
{
"name": "Moto",
"price": "40"
}
]
}
]
},
"after": {
"device": [
{
"id": "1234",
"price": "50",
"features": [
{
"name": "samsung",
"price": "20"
},
{
"name": "Lenovo",
"price": "30"
}
]
},
{
"id": "2158",
"price": "40",
"features": [
{
"name": "samsung",
"price": "30"
}
]
}
]
}
};
// Output JSON which you should need to show the desired output.
var output = {
"devices": [
{
"id": 1234,
"feature": [
{
"name": "1234",
"price": {
"before": 10,
"after": 50
}
},
{
"name": "samsung",
"price": {
"before": 10,
"after": 20
}
},
{
"name": "apple",
"price": {
"before": 10,
"after": 0
}
},
{
"name": "lenovo",
"price": {
"before": 0,
"after": 30
}
}
]
}
]
};
3.) Please try to get the desired output from input yourself as this will help you learn a lot of things in between, as suggested by some please use map, filter, forEach for your requirement.
Hope this helps. Thanks!
You could take a nested approach for grouping.
var data = { before: { device: [{ id: "1234", price: "10", features: [{ name: "samsung", price: "10" }, { name: "Apple", price: "20" }] }, { id: "2154", price: "20", features: [{ name: "samsung", price: "30" }, { name: "Moto", price: "40" }] }] }, after: { device: [{ id: "1234", price: "50", features: [{ name: "samsung", price: "20" }, { name: "Lenovo", price: "30" }] }, { id: "2158", price: "40", features: [{ name: "samsung", price: "30" }] }] } },
cols = Object.fromEntries(Object.keys(data).map(k => [k, 0])),
result = Object.values(Object.entries(data).reduce((r, [col, { device }]) => {
device.forEach(({ id, price, features }) => {
r[id] = r[id] || [{ id, ...cols }];
r[id][0][col] = price;
features.forEach(({ name, price }) => {
let temp = r[id].find(q => q.name === name);
if (!temp) r[id].push(temp = { name, ...cols });
temp[col] = price;
});
});
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use lodash library for grouping
https://lodash.com/docs/3.10.1#groupBy
Comparing 2 objects, and output equivalent values
var has = {"before":[{
name: 'Nokia',
os: 'Andriod',
features: {
camera: "200 MPixel Camera",
battery: "24 hours battery backup",
}
}],
"after":[{
name: 'Samsung',
os: 'Andriod',
features: {
camera: "200 MPixel Camera",
battery: "30 hours battery backup",
}
}]
};
function compare(Pro1, Pro2) {
var Val1 = Object.values(Pro1);
var Val2 = Object.values(Pro2);
var equivalent = [];
var keys = Object.keys(Pro1);
keys.forEach(k => {
if (Pro1.hasOwnProperty(k) && Pro2.hasOwnProperty(k)) {
if (typeof Pro1[k] === 'object') {
let recursiveResult = compare(Pro1[k], Pro2[k]);
equivalent.push(...recursiveResult);
} else if (Pro1[k] === Pro2[k]) {
equivalent.push(Pro1[k]);
}
}
});
return equivalent;
}
let equiv = compare(has["before"], has["after"]);
console.log(equiv);
I have 2 objects:
var set1 = {
name: "2020-02-17T00:00:00",
series: [
{ key: "055", value: 1154.47},
{ key: "056", value: 4132.2},
{ key: "059", value: 1825.33}
]
}
var set2 = {
startdate: "2020-02-17T00:00:00",
series: [
{key: "005",value: 0},
{key: "055",value: 0},
{key: "056",value: 0},
{key: "059",value: 0},
{key: "042",value: 0},
{key: "M002",value: 0}
]
}
How can I push the values of set1 onto set2 without overwriting any of the keys with value 0 as such:
var set2 = {
startdate: "2020-02-17T00:00:00",
series: [
{key: "005", value: 0},
{key: "055", value: 1154.47},
{key: "056", value: 4132.2},
{key: "059", value: 1825.33},
{key: "042", value: 0},
{key: "M002", value: 0}
]
}
Relevant code, you can see here: https://jsfiddle.net/xLr8qoes/1/
set2.series = [ ...set2.series, ...set1.series ];
This will set set2 series to be
[
{
"key": "005",
"value": 0
},
{
"key": "055",
"value": 0
},
{
"key": "056",
"value": 0
},
{
"key": "059",
"value": 0
},
{
"key": "042",
"value": 0
},
{
"key": "M002",
"value": 0
},
{
"key": "055",
"value": 1154.47
},
{
"key": "056",
"value": 4132.2
},
{
"key": "059",
"value": 1825.33
},
{
"key": "055",
"value": 1154.47
},
{
"key": "056",
"value": 4132.2
},
{
"key": "059",
"value": 1825.33
}
]
you can do it in both way
Using spread operator:
let object3=[...object1,...object2]
Using Older javascript:
let returnedTarget = Object.assign({},object1, object2);
Managed to work out a way in the end!
set2.series.forEach(element => {
if (set1.series.filter(e => e.key == element.key).length >= 1){
element.value = set1.series.filter(e => e.key == element.key)[0].value;
}
else element.value = 0;
})
For each object in set2.series it filters set1.series on matching keys. Filter returns an array so if length of array is > 0, that key/value pair exists in set1.
set2 = {
"startdate": "2020-02-17T00:00:00",
"series": [
{key: "005", value: 0},
{key: "042", value: 0},
{key: "055", value: 1154.47},
{key: "056", value: 4132.2},
{key: "059", value: 1825.33},
{key: "M002", value: 0}
]
}
Is there a better/simpler way than this?
I have the following json returned from an ajax request
[
{
"id": "1",
"task": "eat pizza",
"username": "all"
},
{
"id": "2",
"task": "drink soda",
"username": "all"
}
]
I am trying to add specific pieces of data from the json to an existing js array.
this.$todoData2 = [
{
'id': '5',
'text': 'Cheese',
'done': false
},
]
I need to add json key id and its matching value - example: "id": key,
I need to add json key task as text with its matching value - example: "text": key
I need to add a "done": "false" to set so a final result would look like:
this.$todoData2 = [
{
"id": "1",
"text": "eat pizza",
'done': false
}
{
"id": "2",
"text": "drink soda",
'done': false
}
{
'id': '5',
'text': 'Cheese',
'done': false
},
]
I have no examples of what I have tried as I am not sure where to even begin with this. Keep in mind that json may contain a lot more results.
Use .forEach() to iterate over the 'updates' Array, while destructuring the key / value pairs you want to use
Create your object, then .push() it to the Array
const $todoData2 = [
{'id': '5', 'text': 'Cheese', 'done': false},
];
const update = [
{"id": "1", "task": "eat pizza", "username": "all"},
{"id": "2", "task": "drink soda", "username": "all"}
];
update.forEach(({id, task}) => {
$todoData2.push({
'id': id,
'text': task,
'done': false
});
});
console.log($todoData2);
You can merge both arrays and then use map.
let arr = [
{
"id": "1",
"task": "eat pizza",
"username": "all"
},
{
"id": "2",
"task": "drink soda",
"username": "all"
}
]
let todoData2 = [
{
'id': '5',
'text': 'Cheese',
'done': false
},
]
todoData2 = todoData2.concat(arr).map( obj => ({ id:obj.id, text:obj.task || obj.text , done:false }) );
console.log(todoData2)
If the point here is to simply append and rename keys, it may by done as simple as that:
const newItems = [{"id":"1","task":"eat pizza","username":"all"},{"id":"2","task":"drink soda","username":"all"}],
existingItems = [{"id":"5","text":"Cheese","done":false}]
existingItems.push(...newItems.map(({id,task:text}) => ({id,text,done:false})))
console.log(existingItems)
.as-console-wrapper{min-height:100%}