I have an array of dates that I use for mapping. When trying to pass items from a different array (names) using the index, I get undefined values because the "names" array has less entries than the index (dates).
I have a third array with what should be the correct format (format):
let names = [
"chair",
"table",
"door",
"window",
"glass",
"wine",
"car",
"keys",
"dream",
"keyboard",
"vodka",
"pepsi",
"bag",
"ikea",
"mercedes",
"soprano"
];
let format = [3, 3, 1, 5, 4, 4, 3, 5, 13, 10, 3, 5, 5, 2, 2, 10];
let dates = [
"2021-10-04T04:00:00.000Z",
"2021-10-05T04:00:00.000Z",
"2021-10-06T04:00:00.000Z",
"2021-10-13T04:00:00.000Z",
"2021-10-14T04:00:00.000Z",
"2021-10-15T04:00:00.000Z",
"2021-10-15T04:00:00.000Z",
"2021-10-17T22:00:00.000Z",
"2021-10-18T22:00:00.000Z",
"2021-10-19T22:00:00.000Z",
"2021-10-20T22:00:00.000Z",
"2021-10-21T22:00:00.000Z",
"2021-10-17T22:00:00.000Z",
"2021-10-18T22:00:00.000Z",
"2021-10-19T22:00:00.000Z",
"2021-10-20T22:00:00.000Z",
"2021-10-19T04:00:00.000Z",
"2021-10-20T04:00:00.000Z",
"2021-10-21T04:00:00.000Z",
"2021-10-22T04:00:00.000Z",
"2021-10-19T04:00:00.000Z",
"2021-10-20T04:00:00.000Z",
"2021-10-21T04:00:00.000Z",
"2021-10-25T04:00:00.000Z",
"2021-10-26T04:00:00.000Z",
"2021-10-27T04:00:00.000Z",
"2021-10-28T04:00:00.000Z",
"2021-10-29T04:00:00.000Z",
"2021-10-25T04:00:00.000Z",
"2021-10-26T04:00:00.000Z",
"2021-10-27T04:00:00.000Z",
"2021-10-28T04:00:00.000Z",
"2021-10-29T04:00:00.000Z",
"2021-11-01T04:00:00.000Z",
"2021-11-02T04:00:00.000Z",
"2021-11-03T04:00:00.000Z",
"2021-11-04T04:00:00.000Z",
"2021-11-05T04:00:00.000Z",
"2021-11-08T04:00:00.000Z",
"2021-11-09T04:00:00.000Z",
"2021-11-10T04:00:00.000Z",
"2021-11-01T04:00:00.000Z",
"2021-11-02T04:00:00.000Z",
"2021-11-03T04:00:00.000Z",
"2021-11-04T04:00:00.000Z",
"2021-11-05T04:00:00.000Z",
"2021-11-08T04:00:00.000Z",
"2021-11-09T04:00:00.000Z",
"2021-11-10T04:00:00.000Z",
"2021-11-11T04:00:00.000Z",
"2021-11-12T04:00:00.000Z",
"2021-11-11T04:00:00.000Z",
"2021-11-12T04:00:00.000Z",
"2021-11-13T04:00:00.000Z",
"2021-11-15T04:00:00.000Z",
"2021-11-16T04:00:00.000Z",
"2021-11-17T04:00:00.000Z",
"2021-11-18T04:00:00.000Z",
"2021-11-19T04:00:00.000Z",
"2021-11-16T04:00:00.000Z",
"2021-11-17T04:00:00.000Z",
"2021-11-18T04:00:00.000Z",
"2021-11-19T04:00:00.000Z",
"2021-11-20T04:00:00.000Z",
"2021-11-23T04:00:00.000Z",
"2021-11-24T04:00:00.000Z",
"2021-11-23T04:00:00.000Z",
"2021-11-24T04:00:00.000Z",
"2022-01-05T04:00:00.000Z",
"2022-01-06T04:00:00.000Z",
"2022-01-07T04:00:00.000Z",
"2022-01-10T04:00:00.000Z",
"2022-01-11T04:00:00.000Z",
"2022-01-12T04:00:00.000Z",
"2022-01-13T04:00:00.000Z",
"2022-01-14T04:00:00.000Z",
"2022-01-17T04:00:00.000Z",
"2022-01-18T04:00:00.000Z"
];
let app_multiple = dates.map(function combineTitleData(dataItem, index) {
return {
text: 'LR' + dates[index] + ': ' + names[index],
};
});
console.log(app_multiple);
What I would like to achieve is using the "format" array is to follow the patern and use this array to construct the mapping.
The format contains: [3, 3, 1, 5, 4, 4, 3, 5, 13, 10, 3, 5, 5, 2, 2, 10];
I would like to use in the mapping the "names" array like this:
(chair) -> 3 times
(table) -> 3 times
(door) -> 1 time
(window) -> 5 times
(glass) -> 4 times
.... etc
So the output would be like this:
[
{
"text": "LR2021-10-04T04:00:00.000Z: chair"
},
{
"text": "LR2021-10-05T04:00:00.000Z: chair"
},
{
"text": "LR2021-10-06T04:00:00.000Z: chair"
},
{
"text": "LR2021-10-13T04:00:00.000Z: table"
},
{
"text": "LR2021-10-14T04:00:00.000Z: table"
},
{
"text": "LR2021-10-15T04:00:00.000Z: table"
},
{
"text": "LR2021-10-15T04:00:00.000Z: door"
},
{
"text": "LR2021-10-17T22:00:00.000Z: window"
},
{
"text": "LR2021-10-18T22:00:00.000Z: window"
},
{
"text": "LR2021-10-19T22:00:00.000Z: window"
},
{
"text": "LR2021-10-20T22:00:00.000Z: window"
},
{
"text": "LR2021-10-21T22:00:00.000Z: window"
}
.... etc
.... etc
]
Is this possible to do? Thanks.
The first option is:
var numTimesUsed = 0;
var nameIndex = 0;
let app_multiple = dates.map(function combineTitleData(dataItem, index) {
if(format[nameIndex] == numTimesUsed) {
nameIndex++;
numTimesUsed = 0;
}
numTimesUsed++;
return {
text: 'LR' + dates[index] + ': ' + names[nameIndex],
};
});
The second option is (I'm looping through the names map instead of dates for this option):
var dateIndex = 0;
let app_multiple = names.map(function combineTitleData(nameItem, index) {
var datesForName = [];
for(var i = 0; i < format[index]; i++) {
datesForName[i] = {
text: 'LR' + dates[dateIndex] + ': ' + names[index],
};
dateIndex++;
}
return datesForName;
});
You could probably also use some math to avoid extra variables in the second option, but its easier to just have an extra variable.
Related
I have been playing around with some Nested Set Models (NSM). One thing I wanted to do is to be able to generate a NSM from a given JavaScript object.
For example, given the following object:
var data = {
Clothes: {
Jackets: {
Waterproof: true,
Insulated: true
},
Hats: true,
Socks: true
},
}
I'd like to generate an array of objects like so.
[
{
"name": "Clothes",
"lft": 1,
"rgt": 12
},
{
"name": "Jackets",
"lft": 2,
"rgt": 7
},
{
"name": "Waterproof",
"lft": 3,
"rgt": 4
},
{
"name": "Insulated",
"lft": 5,
"rgt": 6
},
{
"name": "Hats",
"lft": 8,
"rgt": 9
},
{
"name": "Socks",
"lft": 10,
"rgt": 11
}
]
That is - a depth first walk through the object, assigning an ID and counting the left and right edge for each object in the hierarchy. So that each node has a unique ID and the correct lft and rgt values for a NSM.
I've tried various approaches but just can't seem to get the result I am after...I had some success by altering the model to use properties for the node name and child nodes - i.e.
var data2 = {
name: "Clothes",
children: [{
name: "Jackets",
children: [{
name: "Waterproof",
}, {
name: "Insulated"
}]
}, {
name: "Hats"
},
{
name: "Socks"
}
]
};
function nestedSet(o, c, l = 0) {
let n = {
name: o.name,
lft: l + 1
};
c.push(n);
let r = n.lft;
for (var x in o.children) {
r = nestedSet(o.children[x], c, r);
}
n.rgt = r + 1;
return n.rgt;
}
let out = [];
nestedSet(data2, out);
console.log(out)
This gives the correct result but requires altering the input data...is there a way to generate the same Nested Set Model using the original data object?
I actually managed to solve this in the end...I just forgot about it for a long while! Basically all that is required is to reclusively pass the Object.entries as kindly suggested in #CherryDT's comment. This way one can resolve the name/children to build the nested set model as required.
var data = {
Clothes: {
Jackets: {
Waterproof: {},
Insulated: {},
},
Hats: {},
Socks: {},
},
};
function ns(node, stack = [], lft = 0) {
var rgt = ++lft;
var item = {
name: node[0],
lft: lft,
};
stack.push(item);
Object.entries(node[1]).forEach(function (c) {
rgt = ns(c, stack, rgt);
});
item.rgt = ++rgt;
return rgt;
}
var result = [];
ns(Object.entries(data)[0], result);
console.log(result);
I have 2 List Objects, and need to remove all items from ListA that contains ListB and return the remaining. My approach is the following:
LIST A
[{
"id": 1,
"name": "ant"
}, {
"id": 2,
"name": "ant2"
}, {
"id": 3,
"name": "ant3"
}, {
"id": 4,
"name": "ant3"
}, {
"id": 5,
"name": "ant3"
}]
LIST B
[{
"id": 1,
"name": "ant"
}, {
"id": 4,
"name": "ant4"
}, {
"id": 3,
"name": "ant3"
} ]
What I have tried:
const xxx = this.listA.filter(x => !listB.includes(x) != null);
Note (Alternative scenario): When there are 2 List that are identical, the expected result is a []. However, in my case its same as a single List.
Here you are :)
const listBSerialized = listB.map(x => JSON.stringify(x))
const xxx = listA.filter(x => !listBSerialized.includes(JSON.stringify(x)));
You'll have the following result:
[ { id: 2, name: 'ant2' },
{ id: 4, name: 'ant3' },
{ id: 5, name: 'ant3' } ]
You can also use models ever you need properties order guarantee on the serialization, sometimes the objects can have the same properties but in a different order (it depends on where is the object coming from):
function Ant(id, name) {
this.id = id
this.name = name
}
listA = [
new Ant(1, "ant"),
new Ant(2, "ant2"),
new Ant(3, "ant3"),
new Ant(4, "ant3"),
new Ant(5, "ant3")
]
listB = [
new Ant(1, "ant"),
new Ant(4, "ant4"),
new Ant(3, "ant3")
]
const listBSerialized = listB.map(x => JSON.stringify(x))
const xxx = listA.filter(x => !listBSerialized.includes(JSON.stringify(x)));
Here is a simple one liner that will handle this task:
const xxx = listA.filter(base => ! listB.some(f => f.id === base.id && f.name === base.name))
where a is ListA and b is ListB array of objects. Basically what you would do is to filter the a array with a condition that there is no object that has the same id and name in b array.
I have the following javascript code which produces the desired results, i.e. returns both the 3rd and 4th objects in objectsArray since they both contain the max distance. However, I'm wondering if there is a way to not have to retype the name of the array when calling objectsArray.filter? I'm not trying to be lazy, just avoiding redundancy and the possibility of introducing a typo.
function meetsMax(obj) {
return obj.distance === Math.max.apply(Math, this.map(function(o) { return o.distance; }));
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
const objMax = objectsArray.filter(meetsMax, objectsArray);
console.log("objMax:", objMax);
I certainly wouldn't mind any other pointers on making the code more efficient and performant.
Function calls in JavaScript have some overhead, so native code is more efficient and performant:
var a = [ { "distance": 1, "name": "first" }, { "distance": 2, "name": "second" },
{ "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" } ]
for (var o = a[0], objMax = [o], m = o.distance, d, i = 1; i < a.length; i++)
if ((d = (o = a[i]).distance) > m) { objMax = [o]; m = d }
else if (d === m) objMax[objMax.length] = o
console.log(JSON.stringify(objMax))
There are also shorter and less efficient alternatives:
var a = [ { "distance": 1, "name": "first" }, { "distance": 2, "name": "second" },
{ "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" } ]
var d, b = []; a.forEach(o => (b[d = o.distance] = b[d] || []).push(o))
console.log(JSON.stringify(b[b.length - 1]))
Why don't you use for loop? It will be faster than your code.
"use strict";
let start = performance.now();
for (let z = 0; z < 1000; z++) {
function meetsMax(obj) {
return obj.distance === Math.max.apply(Math, this.map(function(o) { return o.distance; }));
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
const objMax = objectsArray.filter(meetsMax, objectsArray);
}
let fin = performance.now() - start;
console.log(fin); // 3.25ms
"use strict";
let start = performance.now();
for (let z = 0; z < 1000; z++) {
let a = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
let maxDistance = 0;
let result = [];
for (let i = 0, max = a.length; i < max; i++) {
if (a[i].distance > maxDistance) {
maxDistance = a[i].distance;
}
}
for (let i = 0, max = a.length; i < max; i++) {
if (a[i].distance === maxDistance) {
result.push(a[i]);
}
}
}
let fin = performance.now() - start;
console.log(fin); // 1.28ms
.filter passes three arguments to the array: the current value, the index of the current value and the array itself. So you can change your filter function to:
function meetsMax(obj, index, objectsArray) {
return obj.distance === Math.max.apply(Math, objectsArray.map(function(o) { return o.distance; }));
}
and call .filter with
objectsArray.filter(meetsMax);
Always read the documentation of the functions you are using.
I certainly wouldn't mind any other pointers on making the code more efficient and performant.
If you, compute the maximum distance only once instead of in every iteration of the array. E.g. you could do:
function filterMax(arr, extractor) {
const max = arr.reduce(function(max, item) {
return max < extractor(item) ? extractor(item) : max;
}, extractor(arr[0]));
return arr.filter(function(item) {
return extractor(item) === max;
});
}
and call it as
filterMax(objectsArray, function(obj) { return obj.distance; });
function filterMax(arr, extractor) {
const max = arr.reduce(function(max, item) {
return max < extractor(item) ? extractor(item) : max;
}, extractor(arr[0]));
return arr.filter(function(item) {
return extractor(item) === max;
});
}
const objectsArray = [{ "distance": 1, "name": "first" }, { "distance": 2, "name": "second" }, { "distance": 3, "name": "third" }, { "distance": 3, "name": "fourth" }];
console.log(filterMax(objectsArray, function(obj) {
return obj.distance;
}));
According to MDN's Array.prototype.filter(), the array name is an optional override to the internal value of this.
So to answer the original question:
I'm wondering if there is a way to not have to retype the name of the array when calling objectsArray.filter?
Yes, you can safely leave it out.
var filter = function(x) { if (x > 5) return true; };
var arr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
alert(arr.filter(filter).join(","));
or even simpler (albeit harder to read):
alert([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter(function(x) { if (x > 5) return true; }));
You specifically asked about an object, but you're not filtering objects, you're filtering an array of objects, so same applies.
console.log([ {foo: 1}, {foo: 2}, {foo: 3}, {foo: 4}, {foo: 5}, {foo: 6}, {foo: 7}, {foo: 8}, {foo: 9}, {foo: 10}].filter(function(x) { if (x.foo > 5) return true; }));
I am trying to write a recursive function in javascript but not work properly. i have a json array of objects data where i want to find something based on key then find again based on gotopage key in search object.
like : find orange -> gotopage -> orange_store ->find -> orange_store -> gotopage -> yellow_store -> find so the same process goes in recursively.can you please help where i'm going wrong in my approach.
[
{
"id": 1,
"find": "orange",
"gotopage": "orange_store"
},
{
"id": 2,
"find": "orange_store",
"gotopage": "yellow_store"
},
{
"id": 3,
"find": "black_store",
"gotopage": "black_store"
},
{
"id": 4,
"find": "yellow_store",
"gotopage": "white_store"
},
{
"id": 5,
"find": "black_store",
"gotopage": "red_store"
}
]
function searchRec(search, myArray) {
for (var i = 0; i < myArray.length; i++) {
var res = [];
if (myArray[i].find == search) {
if (myArray[i] !== null) {
console.log(myArray[i]);
res = searchRec(myArray[i].gotopage, myArray);
if (res !== null) {
return res;
}
return myArray[i];
}
}
}
}
function findNode(arr) {
for (i = 0; i < arr.length; i++) {
searchRec(arr[i].find, arr);
break;
}
}
console.log(findNode(json));
output for first iteration but not work for every iteration:
Object {id: 1, find: "orange", gotopage: "orange_store"}
Object {id: 2, find: "orange_store", gotopage: "yellow_store"}
Another example using recursion. I do a simple forEach() to find what you're looking for and store it in variables, log it, and re-call the function with our newly created values. If it doesn't find anything, it returns null and ends.
const data = [
{
"id": 1,
"find": "orange",
"gotopage": "orange_store"
},
{
"id": 2,
"find": "orange_store",
"gotopage": "yellow_store"
},
{
"id": 3,
"find": "black_store",
"gotopage": "black_store"
},
{
"id": 4,
"find": "yellow_store",
"gotopage": "white_store"
},
{
"id": 5,
"find": "black_store",
"gotopage": "red_store"
}
];
function recursiveStore(search, myArray) {
let obj = {}
let newSearch;
data.forEach(store => {
if (search === store.find) {
obj = store
newSearch = store.gotopage
}
})
if (Object.keys(obj).length === 0) {
return null
}
console.log(obj)
recursiveStore(newSearch, myArray)
}
recursiveStore("orange", data)
So I have
var arrays = [
[ Material A, 1, 2, 3, 4, 5 ],
[ Material B, 6, 7, 8, 9, 10 ],
[ Material C, 11, 12, 13, 14, 15 ]
];
and I need to put it in this format which is a multidimensional associative array,
var bigdata = [
{ "Name": "MaterialA", "Row1": 1, "Row2": 2, "Row3": 3, "Row4": 4, "Row5": 5 },
{ "Name": "MaterialB", "Row1": 6, "Row2": 7, "Row3": 8, "Row4": 9, "Row5": 10 },
{ "Name": "MaterialC", "Row1": 11, "Row2": 12, "Row3": 13, "Row4": 14, "Row5": 15 }
];
I am trying
var bigdata = new Array(3);
for (i=0; i<3; i++)
{
// do name
bigdata[i][0] = {"Name" : arrays[i][0]};
for (j=1; j<6; j++ )
{
// rest of that row
}
}
But so far it is not working when I try to store the first "Name": "MaterialA" . What am I doing wrong or can this even be done? Thanks for the help.
This is working for me. Notice I removed the [0] from your bigdata[i][0], and added the row assignment code to your "j" loop.
for (i=0; i<3; i++)
{
// do name
bigdata[i] = {"Name" : arrays[i][0]};
for (j=1; j<6; j++ )
{
// rest of that row
bigdata[i]['Row' + j] = arrays[i][j];
}
}
JSFiddle: http://jsfiddle.net/ub54S/1/
The proper way to set a property of an associative array/object is like this:
bigdata[i]["Property"] = value // this allows dynamic property name
bigdata[i].Property = value // or like this, if property is hard-coded
So in your case, it should be:
bigdata[i] = {} // initialize a blank object
bigdata[i].Name = arrays[i][0];
for ( j=1; j<6; j++ )
bigdata[i]["Row" + j] = arrays[i][j];
Here is a demo: http://jsfiddle.net/56tk5/