Get last value inserted into a Set - javascript

The MDN documentation for Set says that JavaScript Set objects retain insertion order of elements:
Set objects are collections of values, you can iterate its elements in insertion order.
Is there a way to get the last item inserted into a Set object?
var s = new Set();
s.add("Alpha");
s.add("Zeta");
s.add("Beta");
console.log(getLastItem(s)); // prints "Beta"
Edit
It is possible to implement a Linked Set datastructure container class that has the same interface as Set and has the desired capability. See my answer below.

I was not able to find any method to get last value inserted in set from ECMA 2015 Specification, may be they never intended such a method, but you can do something like:
const a = new Set([1, 2, 3]);
a.add(10);
const lastValue = Array.from(a).pop();
Edit:
on second thought, a space efficient solution might be:
function getLastValue(set){
let value;
for(value of set);
return value;
}
const a = new Set([1, 2, 3]);
a.add(10);
console.log('last value: ', getLastValue(a));

Some ideas:
Consider using an array instead of a set. Extracting the last element of an array is easy, e.g.
array[array.length-1];
array.slice(-1)[0];
array.pop(); // <-- This alters the array
If you really need a set, you can convert it to an array when you want to extract the last item, but that will cost time and space.
Iterate the set manually. This will cost time but not as much space as copying into an array. For example (there are probably more elegant ways to do this)
var set = new Set([1, 2, 3]);
var iter = set.values(), prev, curr;
do {
prev = curr;
curr = iter.next();
} while(!curr.done)
var last = prev.value; // 3
Consider inserting the items in reverse order. Then you only need to get the first item in the set, and that's easier:
set.values().next().value;
Subclass Set to add this new functionality:
class MySet extends Set {
add(value) {
super.add(value);
this.last = value;
}
}
var set = new MySet();
set.add(1); set.add(2); set.add(3);
set.last; // 3
Note this will only detect values added with add. To be more complete, it should also detect the latest value when the set is constructed, and update the value when the last item is removed.

Yes, there is a way to do that, you can simply convert the set to an array and pop of the last item
function getLastItem(_set) {
return [..._set].pop();
}
to get keys/values etc, you can do
return [..._set.entries()].pop(); // the entire entry
return [..._set.keys()].pop(); // the key only
return [..._set.values()].pop(); // the value only
If you don't want to create an array, you'd probably have to iterate and get the last value, like this
var last; s.forEach(k => { last = k }); // last === "Beta"
FIDDLE

Just another approach.
Set.prototype.last = function(){
return new Set().add( [...this].pop() );
}
Set.prototype.lastKey = function(){
return [...this.keys()].pop();
}
Set.prototype.lastValue = function(){
return [...this.values()].pop();
}
var lastSet = s.last(); // "Beta"
var lastKey = s.lastKey(); // "Beta"
var lastValue = s.lastValue(); // "Beta"

I have created a replacement for Set, which re-implements the linked functionality of the set, using an underlying Map.
class LinkedSetLink {
constructor(value) {
this.value = value;
this.prev = this;
this.next = this;
}
insertBefore(item) {
const prev = item.prev = this.prev;
const next = item.next = this;
next.prev = item;
prev.next = item;
}
remove() {
const prev = this.prev;
const next = this.next;
next.prev = prev;
prev.next = next;
}
}
class LinkedSet {
constructor(iterable) {
this._map = new Map();
this._pivot = new LinkedSetLink(/* undefined */);
if (iterable) {
this._addAll(iterable);
}
}
_addAll(iterable) {
for (const item of iterable) {
this.add(item);
}
}
has(item) {
return this._map.has(item);
}
add(item) {
if (!this._map.has(item)) {
const link = new LinkedSetLink(item);
this._pivot.insertBefore(link);
this._map.set(item, link);
}
}
delete(item) {
const link = this._map.get(item);
if (link) {
this._map.delete(item);
link.remove();
}
}
clear() {
this._map.clear();
this._pivot.next = this._pivot.prev = this._pivot;
}
get size() {
return this._map.size;
}
values() {
return this._map.keys();
}
keys() {
return this.values();
}
[Symbol.iterator]() {
return this.values();
}
*entries() {
for (const key of this.values()) {
yield [key, key];
}
}
first() {
return this._pivot.next.value;
}
last() {
return this._pivot.prev.value;
}
}
function test1() {
console.log(Array.from(new LinkedSet(["a", "b", "c"]).entries()));
}
function test2() {
console.log(new LinkedSet(["a", "b", "c"]).last());
}
<button onclick="test1()">test entries</button>
<button onclick="test2()">test last</button>

There is no method for accessing the last item, but you can do it as follows
let setOfNumbers = new Set([10]);
setOfNumbers.add(20).add(2);
let lastValue = [...setOfNumbers].pop()

A simple solution, but O(N):
const getLastValueFromSet = (set) => {
for (var value of set);
return value;
}

Related

How to pass a linked-list?

I was given this as a solution to delete all duplicate numbers in a Linkded list. But I don't understand how to pass in the linked list?
var deleteDuplicates = function(head) {
// sets current node to be head of list
let current = head
// runs until we are at the end of the list
while (current !== null && current.next !== null) {
// checks to see if the current value and the next value are the same
if (current.val === current.next.val){
// skips over the duplicate and the next value becomes 2x next
current.next = current.next.next
// current value and the next value are not the same
} else {
// moves to the next node on the list to run through the while again
current = current.next
}
}
// returns the linked list with no duplicates
return head
};
A example to pass a List to function getListSize(). Is this what you want?
class ListNode {
constructor(data) {
this.data = data
this.next = null
}
}
class LinkedList {
constructor(head = null) {
this.head = head
}
}
let node1 = new ListNode(2)
let node2 = new ListNode(5)
node1.next = node2
let list = new LinkedList(node1)
function getListSize(list){
let count = 0;
let node = list.head;
while (node) {
count++;
node = node.next
}
return count;
}
console.log(getListSize(list));
You created a class but don't instantiate it, to do so call the constructor of the class using the new keyword:
const instance = new LinkedList();
then you need to pass the head property of that instance to the function, currently you are sending an undefined value (head doesn't exist outside the deleteDuplicates function's scope).
deleteDuplicates(instance.head);
i also noticed that your function deleteDuplicates won't work for what you want to do, here is a function that should do what you are trying to achieve:
function deleteDuplicates(list) {
if (!Array.isArray(list)) {
console.error(`Given argument is not an Array: ${list}`);
return;
};
return list.map((element, index, array)=>{
if (array.indexOf(element)!==index) return undefined;
return element;
})
.filter((element)=>(element!==undefined));
}
for more informations about what i said:
how classes work: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/class
understand scope: https://developer.mozilla.org/en-US/docs/Glossary/Scope

Using function of an object after grabbing it from array

When I try to grab the object from the array, the type is undefined. Therefore I cannot use a method from the undefined object as it doesn't exist. I am relatively new to JavaScript and I have come straight from Java so the way of retrieving objects is kind of new to me. This is what I currently have.
var fleetAmount = 0;
var fleets = [];
function Fleet(number) {
this.number = number;
this.activities = [];
this.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(this);
}
var getFleet = function(fleetNumber) {
return fleets[fleetAmount - fleetNumber];
}
This is where I try to grab the object and preform the function
const Fl = require(‘fleet.js’);
const fleet = Fl.getFleet(fleetNumber);
fleet.addActivity(activity);
I am also working in Node.js, which is how I am using the require method.
In combination with the answer from #audzzy I changed the getFleet() function so that it would be more efficient. I tested it out and it worked. This is what I used
function getFleet(fleetNumber) {
let result = fleets.filter(function (e) {
return e.number = fleetNumber;
})
return result[0];
}
Thanks for the help! I appreciate it.
you want to create a new fleet object and add it, not "this"..
adding "this" would cause a circular reference, where
this.fleets[i] = this (and all fleets would have the same value)
when calling get fleet, I would check that a fleet was returned from get fleet
in case amount is less than the number you send to getFleet (where according to what you posted: 1 returns the last, 2 returns second to last etc..)..
I hope this explanation makes sense.. anyways, this should work..
var fleets = [];
doStuff();
function doStuff(){
addFleet(1);
addFleet(2);
addFleet(7);
addFleet(3);
// should return null
let fleet1 = getFleetByNumber(5);
// should return the fleet with number 7, and not change the fleet with number 1
let fleet2 = getFleetByNumber(7);
if(fleet2){
fleet2.addActivity("activity");
}
console.log(`fleets: ${JSON.stringify(fleets)} \nfleet1: ${JSON.stringify(fleet1)} \nfleet2: ${JSON.stringify(fleet2)}`);
}
function addFleet(number) {
let fleet = { number: number,
activities: [] };
fleet.addActivity = function (activity) {
this.activities.push(activity);
};
fleets.push(fleet);
}
function getFleetByNumber(fleetNumber) {
return fleets.find(function (e) {
return e.number == fleetNumber;
});
}
function getFleet(fleetNumber) {
let result = null;
if(fleets.length - fleetNumber >= 0){
result = fleets[fleets.length - fleetNumber];
}
return result;
}

Store new value in an array of objects

I want to change the value of an object of an array but seems like values are not saving.I read about immutable objects but there must be somehow a way to do that.I would be glad for any help, thank you.
app.get('/api/reservations',function(req,res) {
Rezervari.getReservations(function(err,reserv){
if(err){
throw err;
}
let datas = reserv
for( var i=0;i < reserv.length ;i++){
let changetime = dateformat(datas[i].data).format("MM-DD-YYYY")
datas[i].data = changetime;
datas[i].data = dateformat(datas[i].data).format("MM-DD-YYYY")
console.log(datas[i].data) // 2018-09-17T21:00:00.000Z
console.log(changetime) // 09-18-2018
}
res.json(datas);
});
});
Edit: The object is reserv or datas(same array).I want to change field dataof reserv from ISO format to MM-DD-YYYY format.Value is changed in var changeTime but in the array value of data is not changed.
This could happen if the objects in the array were proxies. Here's an example of how you could implement something similar:
const handler = {
get: function(obj, prop) {
if (prop === 'data') {
return this._data.toISOString();
}
},
set: function(obj, prop, value) {
if (prop === 'data') {
this._data = new Date(value);
}
}
};
let obj = new Proxy({}, handler);
obj.data = '2018-09-17';
console.log(obj.data); // returns '2018-09-17T00:00:00.000Z'
If this is your case, and you want to avoid the proxy behavior, you could map the original array to a new one:
res.json(datas.map(item => {
return {
data: dateformat(item.data).format("MM-DD-YYYY")
/* extract any other properties you're interested in from the original objects */
};
}));
You could try cloning the Javascript objects, populating a new array and returning it. Here is an example:
let datas = [];
for (let i = 0; i < reserv.length ; i++){
const clone = Object.assign({}, reserv[i]);
clone.data = dateformat(reserv[i].data).format("MM-DD-YYYY");
datas.push(clone);
}
res.json(datas);

passing array to function - dont get updated

Sorry for the question but I am new to JavaScript
i have defined an object
define(['sharedServices/pubsub', 'sharedServices/topics'], function (pubsub,topics) {
'use strict';
function Incident() {
var that = this;
this._dtasks = [];
this.handlePropertyGet = function(enstate, ename) {
if (!this.entityAspect || !this.entityAspect.entityManager || !this.entityAspect.entityManager.isReady) {
enstate = [];
} else {
enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
}
return enstate;
};
Object.defineProperty(this, 'DTasks', {
get: function () {
return this.handlePropertyGet(this._dtasks, "DTasks");
},
set: function (value) { //used only when loading incidents from the server
that.handlePropertySet('DTask', value);
},
enumerable: true
});
}
return {
Incident: Incident
};
});
when I am calling the property DTasks the inner member _dtask is equal to [], even when i enter the get property and i see that the enstate is filled with the objects when the handlePropertyGet is finished and returned to the get scope the _dtasks remains empty, doesn't it supposed to pass as reference?
this._dtasks "points" to an array. If you pass it as a parameter to this.handlePropertyGet, you make enstate refer to the same array.
If you change the array (as in enstate.push("bar")), the change affects this._dtasks too: you are actually changing neither of them, just the array they both point to.
However, the lines
enstate = []
and
enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
don't modify the array you already have. Instead, they create new arrays and change enstate so that it points to them. this._dtasks, however, remains unchanged.
An easy way to fix it would be to change the code inside the getter to
return this.handlePropertyGet("_dtasks", "DTasks");
and handlePropertyGet to
this.handlePropertyGet = function(enstate, ename) {
if (!this.entityAspect || !this.entityAspect.entityManager || !this.entityAspect.entityManager.isReady) {
this[enstate] = [];
} else {
this[enstate] = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
}
return this[enstate];
};
That way, you would be changing the value of this._dtasks directly.
As an alternative, you could achieve the same result by changing enstate = [] to enstate.length = 0 (which clears the array instead of changing the variable. See https://stackoverflow.com/a/1232046/3191224) and enstate = this.entityAspect.[...] to
var newContent = enstate = this.entityAspect.entityManager.executeQueryLocally(new breeze.EntityQuery(ename).where('IncidentID', 'eq', this.IncidentID));
enstate.length = 0;
Array.prototype.push.apply(enstate, newContent);
which clears the array and then pushes all the elements from the other array, effectively replacing the whole content without changing enstate itself.
My guess is that you are trying to do something like this
Javascript
function Incident() {
var reset = false;
this._dtasks = [];
this.handlePropertyGet = function (ename) {
if (reset) {
this._dtasks = [];
} else {
this._dtasks = [1, 2, 3];
}
return this._dtasks;
};
Object.defineProperty(this, 'DTasks', {
get: function () {
return this.handlePropertyGet("DTasks");
},
enumerable: true
});
}
var x = new Incident();
console.log(x.DTasks);
Output
[1, 2, 3]
On jsFiddle
So you can then use this simplified example with the ideas given by #user3191224

Javascript: Getting the newest array

I've been thinking about how to display the newest array (because the array list would update from time to time.). I think there's a default function to it but I can't find it. Anyone knows it?
var bgImg = new Array();
bgImg[0] = "background.jpg";
bgImg[1] = "background2.jpg";
bgImg[2] = "background3.jpg";
bgImg[3] = "background4.jpg";
If you want the last element of the array,
>>> a = ['background1','background2'];
["background1", "background2"]
>>> b = a[a.length-1]
"background2"
You shouldn't need to manually assign indexes. Just do bgImg.push('background2.jpg'), and it will mutate the array for you. In your case the syntax would be...
var last = bgImg[bgImg.length-1]
If you want the newest then you need to make a variable and set it with whatever you update when you push the newest one, if it doesn't become the last one.
You could create a little object to handle this for you.
function creatImageState() {
var images = [];
return {
get latest() {
return images[images.length - 1];
},
set latest (value) {
images.push(value);
}
};
}
var s = creatImageState();
s.latest = "a.png";
s.latest = "b.png";
alert(s.latest);
IE doesn't support getters and setters so you probably need to use this.
function creatImageState() {
var images = [];
return {
getLatest : function() {
return images[images.length - 1]
},
setLatest : function(value) {
images.push(value);
}
};
}
var s = creatImageState();
s.setLatest("a.png");
s.setLatest("b.png");
alert(s.getLatest());

Categories

Resources